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/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { useClient, useFormValue, defineType, defineField, definePlugin } from "sanity";
1
+ import { useFormValue, defineType, defineField, definePlugin } from "sanity";
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
- import { useMemo } from "react";
4
3
  import { Stack, Text, Box } from "@sanity/ui";
4
+ import { useMemo } from "react";
5
5
  const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (title, keywordList) => {
6
6
  if (!title || keywordList.length === 0) return !1;
7
7
  const lowerTitle = title.toLowerCase();
@@ -18,7 +18,7 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
18
18
  if (!title) return !1;
19
19
  const firstWord = title.trim().split(" ")[0].toLowerCase();
20
20
  return stopWords.includes(firstWord);
21
- }, truncate = (text, maxLength) => text.length > maxLength ? text.slice(0, maxLength) + "\u2026" : text, hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title), getMetaTitleValidationMessages = (title, keywords, isParentseoField) => {
21
+ }, truncate = (text, maxLength) => text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text, hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title), getMetaTitleValidationMessages = (title, keywords, isParentseoField) => {
22
22
  const feedback = [], charCount = title?.length || 0;
23
23
  if (!title?.trim())
24
24
  return feedback.push({ text: "Meta Title is empty. Add content to improve SEO.", color: "red" }), feedback;
@@ -186,10 +186,9 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
186
186
  text: "X Description has excessive punctuation \u2014 simplify it.",
187
187
  color: "orange"
188
188
  }), feedback;
189
- }, MetaTitle = (props) => {
190
- useClient({ apiVersion: "2024-05-05" });
191
- const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = useMemo(
192
- () => getMetaTitleValidationMessages(value || "", keywords, isParentseoField),
189
+ }, MetaDescription = (props) => {
190
+ const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = useMemo(() => parent?.keywords || [], [parent?.keywords]), feedbackItems = useMemo(
191
+ () => getMetaDescriptionValidationMessages(value || "", keywords, isParentseoField),
193
192
  [value, keywords, isParentseoField]
194
193
  );
195
194
  return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
@@ -198,21 +197,15 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
198
197
  /* @__PURE__ */ jsx(
199
198
  "div",
200
199
  {
201
- style: {
202
- minWidth: 10,
203
- height: 10,
204
- borderRadius: "50%",
205
- backgroundColor: item.color
206
- }
200
+ style: { width: 10, height: 10, borderRadius: "50%", backgroundColor: item.color }
207
201
  }
208
202
  ),
209
203
  /* @__PURE__ */ jsx(Text, { weight: "bold", muted: !0, size: 14, children: item.text })
210
204
  ] }, item.text)) })
211
205
  ] });
212
- }, MetaDescription = (props) => {
213
- useClient({ apiVersion: "2024-05-05" });
214
- const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = useMemo(
215
- () => getMetaDescriptionValidationMessages(value || "", keywords, isParentseoField),
206
+ }, MetaTitle = (props) => {
207
+ const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = useMemo(() => parent?.keywords || [], [parent?.keywords]), feedbackItems = useMemo(
208
+ () => getMetaTitleValidationMessages(value || "", keywords, isParentseoField),
216
209
  [value, keywords, isParentseoField]
217
210
  );
218
211
  return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
@@ -221,7 +214,12 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
221
214
  /* @__PURE__ */ jsx(
222
215
  "div",
223
216
  {
224
- style: { width: 10, height: 10, borderRadius: "50%", backgroundColor: item.color }
217
+ style: {
218
+ minWidth: 10,
219
+ height: 10,
220
+ borderRadius: "50%",
221
+ backgroundColor: item.color
222
+ }
225
223
  }
226
224
  ),
227
225
  /* @__PURE__ */ jsx(Text, { weight: "bold", muted: !0, size: 14, children: item.text })
@@ -249,10 +247,9 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
249
247
  },
250
248
  children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
251
249
  /* @__PURE__ */ jsx(Text, { size: 1, style: { color: "#006621", fontSize: 12, lineHeight: 1.3, marginBottom: 3 }, children: (() => {
252
- const base = (url || baseUrl)?.replace(/\/+$/, ""), slugStr = String(slug || "").replace(/^\/+/, ""), path2 = [String(prefixFunction ? prefixFunction(rootDoc) : "").replace(
253
- /^\/+|\/+$/g,
254
- ""
255
- ), slugStr].filter(Boolean).join("/"), finalUrl = path2 ? `${base}/${path2}` : base;
250
+ const base = (url || baseUrl)?.replace(/\/+$/, ""), slugStr = String(slug || "").replace(/^\/+/, ""), urlPath = [String(
251
+ prefixFunction ? prefixFunction(rootDoc) : ""
252
+ ).replace(/^\/+|\/+$/g, ""), slugStr].filter(Boolean).join("/"), finalUrl = urlPath ? `${base}/${urlPath}` : base;
256
253
  return /* @__PURE__ */ jsx(
257
254
  "a",
258
255
  {
@@ -355,7 +352,7 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
355
352
  description: "Enter the full URL of the image. Ensure the image is accessible and meets the recommended size of 1200x630px (minimum 600x315px)."
356
353
  },
357
354
  twitterCard: {
358
- title: "Card Type",
355
+ title: "X Card Type",
359
356
  description: ""
360
357
  },
361
358
  twitterSite: {
@@ -388,179 +385,16 @@ const stopWords = ["the", "a", "an", "and", "or", "but"], hasMatchingKeyword = (
388
385
  }
389
386
  }, getFieldInfo = (fieldName, fieldOverrides) => {
390
387
  const fieldInfo = fieldOverrides && fieldOverrides[fieldName] || DEFAULT_FIELD_INFO[fieldName];
391
- return fieldInfo ? { title: fieldInfo.title, description: fieldInfo.description } : { title: "", description: "" };
392
- }, isFieldHidden = (fieldName, config, documentType) => !!(config.defaultHiddenFields?.includes(fieldName) || documentType && config.fieldVisibility?.[documentType]?.hiddenFields?.includes(fieldName)), getFieldHiddenFunction = (fieldName, config) => ({ document }) => {
388
+ return fieldInfo ? { title: fieldInfo.title || "", description: fieldInfo.description || "" } : { title: "", description: "" };
389
+ }, isFieldHidden = (fieldName, config, documentType) => !!(config.defaultHiddenFields?.includes(fieldName) || documentType && config.fieldVisibility?.[documentType]?.hiddenFields?.includes(fieldName)), getFieldHiddenFunction = (fieldName, config) => ({
390
+ document
391
+ }) => {
393
392
  const documentType = document?._type;
394
393
  return isFieldHidden(fieldName, config, documentType);
395
- }, isEmpty = (value) => value == null || typeof value == "string" && value.trim() === "" || Array.isArray(value) && value.length === 0 || typeof value == "object" && !Array.isArray(value) && Object.keys(value).length === 0;
396
- function seoFieldsSchema(config = {}) {
397
- return defineType({
398
- name: "seoFields",
399
- title: "SEO Fields",
400
- type: "object",
401
- fields: [
402
- defineField({
403
- name: "robots",
404
- title: "Robots Settings",
405
- type: "robots",
406
- // Use the separate robots type here
407
- hidden: getFieldHiddenFunction("robots", config)
408
- }),
409
- // 👇 conditionally spread preview field
410
- ...typeof config.seoPreview == "boolean" && config.seoPreview || typeof config.seoPreview == "object" && !isEmpty(config.seoPreview) ? [
411
- defineField({
412
- name: "preview",
413
- title: "SEO Preview",
414
- type: "string",
415
- components: { input: SeoPreview },
416
- options: {
417
- baseUrl: config.baseUrl || "https://www.example.com",
418
- ...typeof config.seoPreview == "object" && config.seoPreview && config.seoPreview.prefix ? { prefix: config.seoPreview.prefix } : {}
419
- },
420
- // Use a readOnly field to prevent editing
421
- // This field is just for preview purposes
422
- initialValue: "",
423
- // Set an initial value
424
- readOnly: !0
425
- })
426
- ] : [],
427
- defineField({
428
- name: "title",
429
- ...getFieldInfo("title", config.fieldOverrides),
430
- // title: 'Meta Title',
431
- type: "string",
432
- // description:
433
- // '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.',
434
- components: {
435
- input: MetaTitle
436
- },
437
- // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),
438
- hidden: getFieldHiddenFunction("title", config)
439
- }),
440
- defineField({
441
- name: "description",
442
- ...getFieldInfo("description", config.fieldOverrides),
443
- // title: 'Meta Description',
444
- // description:
445
- // 'Provide a concise summary of the page content. This description may be used by search engines in search results.',
446
- type: "text",
447
- rows: 3,
448
- components: {
449
- input: MetaDescription
450
- },
451
- // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),
452
- hidden: getFieldHiddenFunction("description", config)
453
- }),
454
- defineField({
455
- name: "metaImage",
456
- ...getFieldInfo("metaImage", config.fieldOverrides),
457
- // title: 'Meta Image',
458
- // description:
459
- // 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',
460
- type: "image",
461
- options: {
462
- hotspot: !0
463
- },
464
- hidden: getFieldHiddenFunction("metaImage", config)
465
- }),
466
- defineField({
467
- name: "metaAttributes",
468
- // title: 'Additional Meta Attributes',
469
- ...getFieldInfo("metaAttributes", config.fieldOverrides),
470
- type: "array",
471
- of: [{ type: "metaAttribute" }],
472
- // description:
473
- // 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
474
- hidden: getFieldHiddenFunction("metaAttributes", config)
475
- }),
476
- defineField({
477
- name: "keywords",
478
- ...getFieldInfo("keywords", config.fieldOverrides),
479
- title: "Keywords",
480
- type: "array",
481
- of: [{ type: "string" }],
482
- description: "Add relevant keywords for this page. These keywords will be used for SEO purposes.",
483
- hidden: getFieldHiddenFunction("keywords", config)
484
- }),
485
- defineField({
486
- name: "canonicalUrl",
487
- ...getFieldInfo("canonicalUrl", config.fieldOverrides),
488
- title: "Canonical URL",
489
- type: "url",
490
- description: "Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.",
491
- hidden: getFieldHiddenFunction("canonicalUrl", config)
492
- }),
493
- defineField({
494
- name: "openGraph",
495
- title: "Open Graph Settings",
496
- type: "openGraph"
497
- }),
498
- defineField({
499
- name: "twitter",
500
- title: "X (Formerly Twitter) Card Settings",
501
- type: "twitter"
502
- })
503
- ]
504
- });
505
- }
506
- var metaAttribute = defineType({
507
- name: "metaAttribute",
508
- title: "Meta Attribute",
509
- type: "object",
510
- fields: [
511
- defineField({
512
- name: "key",
513
- title: "Attribute Name",
514
- type: "string"
515
- }),
516
- defineField({
517
- name: "type",
518
- title: "Attribute Value Type",
519
- type: "string",
520
- options: {
521
- list: [
522
- { title: "String", value: "string" },
523
- { title: "Image", value: "image" }
524
- ]
525
- },
526
- initialValue: "string"
527
- }),
528
- defineField({
529
- name: "value",
530
- title: "Attribute Value",
531
- type: "string",
532
- hidden: ({ parent }) => parent?.type === "image"
533
- }),
534
- defineField({
535
- name: "image",
536
- title: "Attribute Image Value",
537
- type: "image",
538
- hidden: ({ parent }) => parent?.type === "string"
539
- })
540
- ],
541
- preview: {
542
- select: {
543
- attributeName: "key",
544
- attributeValueString: "value",
545
- attributeValueImage: "image"
546
- },
547
- prepare({
548
- attributeName,
549
- attributeValueString,
550
- attributeValueImage
551
- }) {
552
- return {
553
- title: attributeName || "No Attribute Name",
554
- subtitle: attributeValueString ? `Value: ${attributeValueString}` : attributeValueImage ? "Value: [Image]" : "No Attribute Value",
555
- media: attributeValueImage
556
- };
557
- }
558
- }
559
- });
560
- const OgTitle = (props) => {
561
- const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = useMemo(
562
- () => getOgTitleValidation(value || "", keywords, isParentseoField),
563
- [value, keywords]
394
+ }, isEmpty = (value) => value == null || typeof value == "string" && value.trim() === "" || Array.isArray(value) && value.length === 0 || typeof value == "object" && !Array.isArray(value) && Object.keys(value).length === 0, OgDescription = (props) => {
395
+ const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = useMemo(() => parent?.keywords || [], [parent?.keywords]), feedbackItems = useMemo(
396
+ () => getOgDescriptionValidation(value || "", keywords, isParentseoField),
397
+ [value, keywords, isParentseoField]
564
398
  );
565
399
  return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
566
400
  renderDefault(props),
@@ -579,9 +413,9 @@ const OgTitle = (props) => {
579
413
  /* @__PURE__ */ jsx(Text, { weight: "bold", muted: !0, size: 14, children: item.text })
580
414
  ] }, item.text)) })
581
415
  ] });
582
- }, OgDescription = (props) => {
583
- const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = useMemo(
584
- () => getOgDescriptionValidation(value || "", keywords, isParentseoField),
416
+ }, OgTitle = (props) => {
417
+ const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = useMemo(() => parent?.keywords || [], [parent?.keywords]), feedbackItems = useMemo(
418
+ () => getOgTitleValidation(value || "", keywords, isParentseoField),
585
419
  [value, keywords, isParentseoField]
586
420
  );
587
421
  return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
@@ -689,21 +523,31 @@ function openGraph(config = {}) {
689
523
  description: "A description of the image for accessibility purposes."
690
524
  })
691
525
  ],
692
- hidden: ({ parent }) => parent?.imageType !== "upload"
526
+ hidden: (context) => {
527
+ const { parent } = context;
528
+ if (parent?.imageType !== "upload") return !0;
529
+ const hiddenFn = getFieldHiddenFunction("openGraphImage", config);
530
+ return typeof hiddenFn == "function" ? hiddenFn(context) : hiddenFn;
531
+ }
693
532
  }),
694
533
  defineField({
695
534
  name: "imageUrl",
696
535
  ...getFieldInfo("openGraphImageUrl", config.fieldOverrides),
697
536
  type: "url",
698
- hidden: ({ parent }) => parent?.imageType !== "url"
537
+ hidden: (context) => {
538
+ const { parent } = context;
539
+ if (parent?.imageType !== "url") return !0;
540
+ const hiddenFn = getFieldHiddenFunction("openGraphImage", config);
541
+ return typeof hiddenFn == "function" ? hiddenFn(context) : hiddenFn;
542
+ }
699
543
  })
700
544
  ]
701
545
  });
702
546
  }
703
- const TwitterTitle = (props) => {
704
- const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = useMemo(
705
- () => getTwitterTitleValidation(value || "", keywords, isParentseoField),
706
- [value, keywords]
547
+ const TwitterDescription = (props) => {
548
+ const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = useMemo(() => parent?.keywords || [], [parent?.keywords]), feedbackItems = useMemo(
549
+ () => getTwitterDescriptionValidation(value || "", keywords, isParentseoField),
550
+ [value, keywords, isParentseoField]
707
551
  );
708
552
  return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
709
553
  renderDefault(props),
@@ -722,10 +566,10 @@ const TwitterTitle = (props) => {
722
566
  /* @__PURE__ */ jsx(Text, { weight: "bold", muted: !0, size: 14, children: item.text })
723
567
  ] }, item.text)) })
724
568
  ] });
725
- }, TwitterDescription = (props) => {
726
- const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = parent?.keywords || [], feedbackItems = useMemo(
727
- () => getTwitterDescriptionValidation(value || "", keywords, isParentseoField),
728
- [value, keywords]
569
+ }, TwitterTitle = (props) => {
570
+ const { value, renderDefault, path } = props, parent = useFormValue([path[0]]), isParentseoField = parent && parent?._type === "seoFields", keywords = useMemo(() => parent?.keywords || [], [parent?.keywords]), feedbackItems = useMemo(
571
+ () => getTwitterTitleValidation(value || "", keywords, isParentseoField),
572
+ [value, keywords, isParentseoField]
729
573
  );
730
574
  return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
731
575
  renderDefault(props),
@@ -825,38 +669,180 @@ function twitter(config = {}) {
825
669
  type: "string",
826
670
  description: "A description of the image for accessibility purposes."
827
671
  })
828
- ]
672
+ ],
673
+ hidden: (context) => {
674
+ const { parent } = context;
675
+ if (parent?.imageType !== "upload") return !0;
676
+ const hiddenFn = getFieldHiddenFunction("twitterImage", config);
677
+ return typeof hiddenFn == "function" ? hiddenFn(context) : hiddenFn;
678
+ }
829
679
  }),
830
680
  defineField({
831
681
  name: "imageUrl",
832
682
  ...getFieldInfo("twitterImageUrl", config.fieldOverrides),
833
683
  type: "url",
834
- hidden: ({ parent }) => parent?.imageType !== "url"
684
+ hidden: (context) => {
685
+ const { parent } = context;
686
+ if (parent?.imageType !== "url") return !0;
687
+ const hiddenFn = getFieldHiddenFunction("twitterImage", config);
688
+ return typeof hiddenFn == "function" ? hiddenFn(context) : hiddenFn;
689
+ }
835
690
  })
836
691
  ]
837
692
  });
838
693
  }
839
- var robots = defineType({
840
- name: "robots",
841
- title: "Robots Settings",
694
+ function seoFieldsSchema(config = {}) {
695
+ return defineType({
696
+ name: "seoFields",
697
+ title: "SEO Fields",
698
+ type: "object",
699
+ fields: [
700
+ defineField({
701
+ name: "robots",
702
+ title: "Robots Settings",
703
+ type: "robots",
704
+ // Use the separate robots type here
705
+ hidden: getFieldHiddenFunction("robots", config)
706
+ }),
707
+ // 👇 conditionally spread preview field
708
+ ...typeof config.seoPreview == "boolean" && config.seoPreview || typeof config.seoPreview == "object" && !isEmpty(config.seoPreview) ? [
709
+ defineField({
710
+ name: "preview",
711
+ title: "SEO Preview",
712
+ type: "string",
713
+ components: { input: SeoPreview },
714
+ options: {
715
+ baseUrl: config.baseUrl || "https://www.example.com",
716
+ ...typeof config.seoPreview == "object" && config.seoPreview && config.seoPreview.prefix ? { prefix: config.seoPreview.prefix } : {}
717
+ },
718
+ // Use a readOnly field to prevent editing
719
+ // This field is just for preview purposes
720
+ initialValue: "",
721
+ // Set an initial value
722
+ readOnly: !0
723
+ })
724
+ ] : [],
725
+ defineField({
726
+ name: "title",
727
+ ...getFieldInfo("title", config.fieldOverrides),
728
+ // title: 'Meta Title',
729
+ type: "string",
730
+ // description:
731
+ // '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.',
732
+ components: {
733
+ input: MetaTitle
734
+ },
735
+ // validation: (Rule) => Rule.max(60).warning('Meta title should be under 60 characters.'),
736
+ hidden: getFieldHiddenFunction("title", config)
737
+ }),
738
+ defineField({
739
+ name: "description",
740
+ ...getFieldInfo("description", config.fieldOverrides),
741
+ // title: 'Meta Description',
742
+ // description:
743
+ // 'Provide a concise summary of the page content. This description may be used by search engines in search results.',
744
+ type: "text",
745
+ rows: 3,
746
+ components: {
747
+ input: MetaDescription
748
+ },
749
+ // validation: (Rule) => Rule.max(160).warning('Meta description should be under 160 characters.'),
750
+ hidden: getFieldHiddenFunction("description", config)
751
+ }),
752
+ defineField({
753
+ name: "metaImage",
754
+ ...getFieldInfo("metaImage", config.fieldOverrides),
755
+ // title: 'Meta Image',
756
+ // description:
757
+ // 'Upload an image that represents the content of the page. This image may be used in social media previews and search engine results.',
758
+ type: "image",
759
+ options: {
760
+ hotspot: !0
761
+ },
762
+ hidden: getFieldHiddenFunction("metaImage", config)
763
+ }),
764
+ defineField({
765
+ name: "metaAttributes",
766
+ // title: 'Additional Meta Attributes',
767
+ ...getFieldInfo("metaAttributes", config.fieldOverrides),
768
+ type: "array",
769
+ of: [{ type: "metaAttribute" }],
770
+ // description:
771
+ // 'Add custom meta attributes to the head of the document for additional SEO and social media integration.',
772
+ hidden: getFieldHiddenFunction("metaAttributes", config)
773
+ }),
774
+ defineField({
775
+ name: "keywords",
776
+ ...getFieldInfo("keywords", config.fieldOverrides),
777
+ title: "Keywords",
778
+ type: "array",
779
+ of: [{ type: "string" }],
780
+ description: "Add relevant keywords for this page. These keywords will be used for SEO purposes.",
781
+ hidden: getFieldHiddenFunction("keywords", config)
782
+ }),
783
+ defineField({
784
+ name: "canonicalUrl",
785
+ ...getFieldInfo("canonicalUrl", config.fieldOverrides),
786
+ title: "Canonical URL",
787
+ type: "url",
788
+ description: "Specify the canonical URL for this page. This helps prevent duplicate content issues by indicating the preferred version of a page.",
789
+ hidden: getFieldHiddenFunction("canonicalUrl", config)
790
+ }),
791
+ openGraph(config),
792
+ twitter(config)
793
+ ]
794
+ });
795
+ }
796
+ var metaAttribute = defineType({
797
+ name: "metaAttribute",
798
+ title: "Meta Attribute",
842
799
  type: "object",
843
800
  fields: [
844
801
  defineField({
845
- name: "noIndex",
846
- title: "No Index",
847
- type: "boolean",
848
- initialValue: !1,
849
- description: "Enable this to prevent search engines from indexing this page. The page will not appear in search results."
802
+ name: "key",
803
+ title: "Attribute Name",
804
+ type: "string"
850
805
  }),
851
806
  defineField({
852
- name: "noFollow",
853
- title: "No Follow",
854
- type: "boolean",
855
- initialValue: !1,
856
- description: "Enable this to prevent search engines from following links on this page. Links will not pass SEO value."
807
+ name: "type",
808
+ title: "Attribute Value Type",
809
+ type: "string",
810
+ options: {
811
+ list: [
812
+ { title: "String", value: "string" },
813
+ { title: "Image", value: "image" }
814
+ ]
815
+ },
816
+ initialValue: "string"
817
+ }),
818
+ defineField({
819
+ name: "value",
820
+ title: "Attribute Value",
821
+ type: "string",
822
+ hidden: ({ parent }) => parent?.type === "image"
823
+ }),
824
+ defineField({
825
+ name: "image",
826
+ title: "Attribute Image Value",
827
+ type: "image",
828
+ hidden: ({ parent }) => parent?.type === "string"
857
829
  })
858
830
  ],
859
- description: "Select how search engines should index and follow links on this page."
831
+ preview: {
832
+ select: {
833
+ attributeName: "key",
834
+ attributeValueString: "value",
835
+ attributeValueImage: "image"
836
+ },
837
+ prepare({ attributeName, attributeValueString, attributeValueImage }) {
838
+ let subtitle = "";
839
+ return attributeValueString ? subtitle = `Value: ${attributeValueString}` : attributeValueImage ? subtitle = "Value: [Image]" : subtitle = "No Attribute Value", {
840
+ title: attributeName || "No Attribute Name",
841
+ subtitle,
842
+ media: attributeValueImage
843
+ };
844
+ }
845
+ }
860
846
  }), metaTag = defineType({
861
847
  name: "metaTag",
862
848
  title: "Meta Tag",
@@ -870,6 +856,27 @@ var robots = defineType({
870
856
  description: "Add custom meta attributes to the head of the document for additional SEO and social media integration."
871
857
  }
872
858
  ]
859
+ }), robots = defineType({
860
+ name: "robots",
861
+ title: "Robots Settings",
862
+ type: "object",
863
+ fields: [
864
+ defineField({
865
+ name: "noIndex",
866
+ title: "No Index",
867
+ type: "boolean",
868
+ initialValue: !1,
869
+ description: "Enable this to prevent search engines from indexing this page. The page will not appear in search results."
870
+ }),
871
+ defineField({
872
+ name: "noFollow",
873
+ title: "No Follow",
874
+ type: "boolean",
875
+ initialValue: !1,
876
+ description: "Enable this to prevent search engines from following links on this page. Links will not pass SEO value."
877
+ })
878
+ ],
879
+ description: "Select how search engines should index and follow links on this page."
873
880
  });
874
881
  function types(config = {}) {
875
882
  return [
@@ -890,36 +897,10 @@ const seofields = definePlugin((config = {}) => ({
890
897
  types: types(config)
891
898
  // pass config down to schemas
892
899
  }
893
- })), 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 = {
894
- title: {
895
- maxLength: 70,
896
- warningLength: 60
897
- },
898
- description: {
899
- maxLength: 160,
900
- warningLength: 150
901
- },
902
- openGraphTitle: {
903
- maxLength: 95
904
- },
905
- openGraphDescription: {
906
- maxLength: 200
907
- },
908
- twitterTitle: {
909
- maxLength: 70
910
- },
911
- twitterDescription: {
912
- maxLength: 200
913
- }
914
- };
900
+ }));
915
901
  export {
916
902
  types as allSchemas,
917
903
  seofields as default,
918
- defaultSeoValidationRules,
919
- isMetaAttribute,
920
- isOpenGraphSettings,
921
- isSeoFields,
922
- isTwitterCardSettings,
923
904
  metaAttribute as metaAttributeSchema,
924
905
  metaTag as metaTagSchema,
925
906
  openGraph as openGraphSchema,