sanity-plugin-seofields 1.6.4 → 1.6.5

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.
@@ -1,4 +1,4 @@
1
- import { truncate } from './chunk-25JLWVEU.js';
1
+ import { truncate } from './chunk-HDZZQCH7.js';
2
2
  import './chunk-2NMEKWO5.js';
3
3
  import { Box } from '@sanity/ui';
4
4
  import { useState, useEffect } from 'react';
@@ -160,12 +160,13 @@ var SeoPreview = (props) => {
160
160
  /* @__PURE__ */ jsxs(PreviewBody, { children: [
161
161
  /* @__PURE__ */ jsx(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
162
162
  /* @__PURE__ */ jsx(SerpTitle, { children: title && title.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
163
- truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 2 : 0))),
163
+ truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 3 : 0))),
164
164
  titleSuffix && /* @__PURE__ */ jsxs(
165
165
  "span",
166
166
  {
167
167
  style: titleSuffixInheritColor ? void 0 : { color: "#70757a", fontWeight: 400 },
168
168
  children: [
169
+ " ",
169
170
  "| ",
170
171
  titleSuffix
171
172
  ]
@@ -179,5 +180,5 @@ var SeoPreview = (props) => {
179
180
  var SeoPreview_default = SeoPreview;
180
181
 
181
182
  export { SeoPreview_default as default };
182
- //# sourceMappingURL=SeoPreview-XVAZYHCL.js.map
183
- //# sourceMappingURL=SeoPreview-XVAZYHCL.js.map
183
+ //# sourceMappingURL=SeoPreview-KEGGQSPJ.js.map
184
+ //# sourceMappingURL=SeoPreview-KEGGQSPJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/SeoPreview.tsx"],"names":["styled","useClient","useState","useEffect","useFormValue","jsx","Box","jsxs","Fragment","truncate"],"mappings":"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","file":"SeoPreview-PYYVZMY3.cjs","sourcesContent":["import {Box} from '@sanity/ui'\nimport {type ReactElement, useEffect, useState} from 'react'\nimport {StringInputProps, useClient, useFormValue} from 'sanity'\nimport styled from 'styled-components'\n\nimport {truncate} from '../utils/seoUtils'\n\nconst PreviewContainer = styled.div`\n max-width: 600px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: #ffffff;\n border: 1px solid #dadce0;\n border-radius: 8px;\n overflow: hidden;\n`\n\nconst PreviewHeader = styled.div`\n background: #f8f9fa;\n padding: 12px 16px;\n border-bottom: 1px solid #dadce0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n`\n\nconst PreviewBody = styled.div`\n padding: 16px;\n`\n\nconst SerpUrl = styled.p`\n margin: 0 0 4px;\n color: #006621;\n font-size: 13px;\n line-height: 1.4;\n word-break: break-word;\n`\n\nconst SerpTitle = styled.h3`\n margin: 0 0 8px;\n color: #1a0dab;\n font-size: 18px;\n font-weight: 500;\n line-height: 1.4;\n word-break: break-word;\n\n &:hover {\n text-decoration: underline;\n }\n`\n\nconst SerpDescription = styled.p`\n margin: 0;\n color: #545454;\n font-size: 14px;\n line-height: 1.6;\n word-break: break-word;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n`\n\nconst LiveIndicator = styled.span`\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: #4f46e5;\n background: #f0f4ff;\n padding: 4px 8px;\n border-radius: 4px;\n`\n\nconst SeoPreview = (props: StringInputProps): ReactElement => {\n const {path, schemaType} = props\n const {options} = schemaType as {\n options?: {\n baseUrl?: string\n apiVersion?: string\n prefix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffixInheritColor?: boolean\n titleSuffixQuery?: string\n }\n }\n const baseUrl = options?.baseUrl || 'https://www.example.com'\n const prefixFunction = options?.prefix as\n | ((doc: {_type?: string} & Record<string, unknown>) => string)\n | undefined\n const titleSuffixOption = options?.titleSuffix\n const titleSuffixInheritColor = options?.titleSuffixInheritColor ?? false\n const titleSuffixQuery = options?.titleSuffixQuery\n\n const client = useClient({apiVersion: options?.apiVersion ?? '2024-01-01'})\n const [groqTitleSuffix, setGroqTitleSuffix] = useState<string>('')\n\n useEffect(() => {\n if (!titleSuffixQuery) return\n client\n .fetch<string>(titleSuffixQuery)\n .then((result) => {\n setGroqTitleSuffix(result === null || result === undefined ? '' : String(result))\n })\n .catch(() => {\n setGroqTitleSuffix('')\n })\n }, [titleSuffixQuery, client])\n const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n const rootDoc: {\n slug?: {current: string}\n } = useFormValue([]) || {\n slug: {current: ''},\n }\n const slug: string = rootDoc?.slug?.current || ''\n\n const {\n title,\n description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n const getTitleSuffix = (): string => {\n if (titleSuffixQuery) return groqTitleSuffix\n if (!titleSuffixOption) return ''\n if (typeof titleSuffixOption === 'function') {\n return titleSuffixOption(rootDoc as {_type?: string} & Record<string, unknown>)\n }\n return titleSuffixOption\n }\n const titleSuffix: string = getTitleSuffix()\n\n // Build full URL\n const base = (url || baseUrl)?.replace(/\\/+$/, '')\n const slugStr = String(slug || '').replace(/^\\/+/, '')\n const pref = String(\n prefixFunction ? prefixFunction(rootDoc as {slug?: {current: string}}) : '',\n ).replace(/^\\/+|\\/+$/g, '')\n const urlPath = [pref, slugStr].filter(Boolean).join('/')\n const finalUrl = urlPath ? `${base}/${urlPath}` : base\n\n // Extract domain for display\n const domain = (() => {\n try {\n const u = new URL(finalUrl || base)\n return u.hostname\n } catch {\n return 'example.com'\n }\n })()\n\n // Format URL display with › separator\n const urlDisplay = `${domain}${urlPath ? ` › ${urlPath.split('/').slice(-1)[0]}` : ''}`\n\n return (\n <Box padding={3}>\n <PreviewContainer>\n <PreviewHeader>\n <span\n style={{\n fontSize: '11px',\n color: '#5f6368',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}\n >\n Search Preview\n </span>\n <LiveIndicator>\n <span\n style={{\n width: '4px',\n height: '4px',\n borderRadius: '50%',\n backgroundColor: '#4f46e5',\n display: 'inline-block',\n }}\n />\n Live\n </LiveIndicator>\n </PreviewHeader>\n\n <PreviewBody>\n <SerpUrl>{finalUrl ? urlDisplay : 'example.com › page-url'}</SerpUrl>\n <SerpTitle>\n {title && title.length > 0 ? (\n <>\n {truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 2 : 0)))}\n {titleSuffix && (\n <span\n style={\n titleSuffixInheritColor ? undefined : {color: '#70757a', fontWeight: 400}\n }\n >\n | {titleSuffix}\n </span>\n )}\n </>\n ) : (\n 'Your SEO Title will appear here'\n )}\n </SerpTitle>\n <SerpDescription>\n {description && description.length > 0\n ? truncate(description, 160)\n : 'Your meta description will show up here. Make it compelling!'}\n </SerpDescription>\n </PreviewBody>\n </PreviewContainer>\n </Box>\n )\n}\n\nexport default SeoPreview\n"]}
1
+ {"version":3,"sources":["../src/components/SeoPreview.tsx"],"names":[],"mappings":"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","file":"SeoPreview-KEGGQSPJ.js","sourcesContent":["import {Box} from '@sanity/ui'\nimport {type ReactElement, useEffect, useState} from 'react'\nimport {StringInputProps, useClient, useFormValue} from 'sanity'\nimport styled from 'styled-components'\n\nimport {truncate} from '../utils/seoUtils'\n\nconst PreviewContainer = styled.div`\n max-width: 600px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: #ffffff;\n border: 1px solid #dadce0;\n border-radius: 8px;\n overflow: hidden;\n`\n\nconst PreviewHeader = styled.div`\n background: #f8f9fa;\n padding: 12px 16px;\n border-bottom: 1px solid #dadce0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n`\n\nconst PreviewBody = styled.div`\n padding: 16px;\n`\n\nconst SerpUrl = styled.p`\n margin: 0 0 4px;\n color: #006621;\n font-size: 13px;\n line-height: 1.4;\n word-break: break-word;\n`\n\nconst SerpTitle = styled.h3`\n margin: 0 0 8px;\n color: #1a0dab;\n font-size: 18px;\n font-weight: 500;\n line-height: 1.4;\n word-break: break-word;\n\n &:hover {\n text-decoration: underline;\n }\n`\n\nconst SerpDescription = styled.p`\n margin: 0;\n color: #545454;\n font-size: 14px;\n line-height: 1.6;\n word-break: break-word;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n`\n\nconst LiveIndicator = styled.span`\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: #4f46e5;\n background: #f0f4ff;\n padding: 4px 8px;\n border-radius: 4px;\n`\n\nconst SeoPreview = (props: StringInputProps): ReactElement => {\n const {path, schemaType} = props\n const {options} = schemaType as {\n options?: {\n baseUrl?: string\n apiVersion?: string\n prefix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffixInheritColor?: boolean\n titleSuffixQuery?: string\n }\n }\n const baseUrl = options?.baseUrl || 'https://www.example.com'\n const prefixFunction = options?.prefix as\n | ((doc: {_type?: string} & Record<string, unknown>) => string)\n | undefined\n const titleSuffixOption = options?.titleSuffix\n const titleSuffixInheritColor = options?.titleSuffixInheritColor ?? false\n const titleSuffixQuery = options?.titleSuffixQuery\n\n const client = useClient({apiVersion: options?.apiVersion ?? '2024-01-01'})\n const [groqTitleSuffix, setGroqTitleSuffix] = useState<string>('')\n\n useEffect(() => {\n if (!titleSuffixQuery) return\n client\n .fetch<string>(titleSuffixQuery)\n .then((result) => {\n setGroqTitleSuffix(result === null || result === undefined ? '' : String(result))\n })\n .catch(() => {\n setGroqTitleSuffix('')\n })\n }, [titleSuffixQuery, client])\n const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n const rootDoc: {\n slug?: {current: string}\n } = useFormValue([]) || {\n slug: {current: ''},\n }\n const slug: string = rootDoc?.slug?.current || ''\n\n const {\n title,\n description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n const getTitleSuffix = (): string => {\n if (titleSuffixQuery) return groqTitleSuffix\n if (!titleSuffixOption) return ''\n if (typeof titleSuffixOption === 'function') {\n return titleSuffixOption(rootDoc as {_type?: string} & Record<string, unknown>)\n }\n return titleSuffixOption\n }\n const titleSuffix: string = getTitleSuffix()\n\n // Build full URL\n const base = (url || baseUrl)?.replace(/\\/+$/, '')\n const slugStr = String(slug || '').replace(/^\\/+/, '')\n const pref = String(\n prefixFunction ? prefixFunction(rootDoc as {slug?: {current: string}}) : '',\n ).replace(/^\\/+|\\/+$/g, '')\n const urlPath = [pref, slugStr].filter(Boolean).join('/')\n const finalUrl = urlPath ? `${base}/${urlPath}` : base\n\n // Extract domain for display\n const domain = (() => {\n try {\n const u = new URL(finalUrl || base)\n return u.hostname\n } catch {\n return 'example.com'\n }\n })()\n\n // Format URL display with › separator\n const urlDisplay = `${domain}${urlPath ? ` › ${urlPath.split('/').slice(-1)[0]}` : ''}`\n\n return (\n <Box padding={3}>\n <PreviewContainer>\n <PreviewHeader>\n <span\n style={{\n fontSize: '11px',\n color: '#5f6368',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}\n >\n Search Preview\n </span>\n <LiveIndicator>\n <span\n style={{\n width: '4px',\n height: '4px',\n borderRadius: '50%',\n backgroundColor: '#4f46e5',\n display: 'inline-block',\n }}\n />\n Live\n </LiveIndicator>\n </PreviewHeader>\n\n <PreviewBody>\n <SerpUrl>{finalUrl ? urlDisplay : 'example.com › page-url'}</SerpUrl>\n <SerpTitle>\n {title && title.length > 0 ? (\n <>\n {truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 3 : 0)))}\n {titleSuffix && (\n <span\n style={\n titleSuffixInheritColor ? undefined : {color: '#70757a', fontWeight: 400}\n }\n >\n {' '}\n | {titleSuffix}\n </span>\n )}\n </>\n ) : (\n 'Your SEO Title will appear here'\n )}\n </SerpTitle>\n <SerpDescription>\n {description && description.length > 0\n ? truncate(description, 160)\n : 'Your meta description will show up here. Make it compelling!'}\n </SerpDescription>\n </PreviewBody>\n </PreviewContainer>\n </Box>\n )\n}\n\nexport default SeoPreview\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkIFDLKZET_cjs = require('./chunk-IFDLKZET.cjs');
3
+ var chunkZBHLMQTS_cjs = require('./chunk-ZBHLMQTS.cjs');
4
4
  require('./chunk-S367Y35J.cjs');
5
5
  var ui = require('@sanity/ui');
6
6
  var react = require('react');
@@ -166,24 +166,25 @@ var SeoPreview = (props) => {
166
166
  /* @__PURE__ */ jsxRuntime.jsxs(PreviewBody, { children: [
167
167
  /* @__PURE__ */ jsxRuntime.jsx(SerpUrl, { children: finalUrl ? urlDisplay : "example.com \u203A page-url" }),
168
168
  /* @__PURE__ */ jsxRuntime.jsx(SerpTitle, { children: title && title.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
169
- chunkIFDLKZET_cjs.truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 2 : 0))),
169
+ chunkZBHLMQTS_cjs.truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 3 : 0))),
170
170
  titleSuffix && /* @__PURE__ */ jsxRuntime.jsxs(
171
171
  "span",
172
172
  {
173
173
  style: titleSuffixInheritColor ? void 0 : { color: "#70757a", fontWeight: 400 },
174
174
  children: [
175
+ " ",
175
176
  "| ",
176
177
  titleSuffix
177
178
  ]
178
179
  }
179
180
  )
180
181
  ] }) : "Your SEO Title will appear here" }),
181
- /* @__PURE__ */ jsxRuntime.jsx(SerpDescription, { children: description && description.length > 0 ? chunkIFDLKZET_cjs.truncate(description, 160) : "Your meta description will show up here. Make it compelling!" })
182
+ /* @__PURE__ */ jsxRuntime.jsx(SerpDescription, { children: description && description.length > 0 ? chunkZBHLMQTS_cjs.truncate(description, 160) : "Your meta description will show up here. Make it compelling!" })
182
183
  ] })
183
184
  ] }) });
184
185
  };
185
186
  var SeoPreview_default = SeoPreview;
186
187
 
187
188
  module.exports = SeoPreview_default;
188
- //# sourceMappingURL=SeoPreview-PYYVZMY3.cjs.map
189
- //# sourceMappingURL=SeoPreview-PYYVZMY3.cjs.map
189
+ //# sourceMappingURL=SeoPreview-WYH7NYNM.cjs.map
190
+ //# sourceMappingURL=SeoPreview-WYH7NYNM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/SeoPreview.tsx"],"names":["styled","useClient","useState","useEffect","useFormValue","jsx","Box","jsxs","Fragment","truncate"],"mappings":";;;;;;;;;;;;;;AAOA,IAAM,mBAAmBA,uBAAA,CAAO,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAShC,IAAM,gBAAgBA,uBAAA,CAAO,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAU7B,IAAM,cAAcA,uBAAA,CAAO,GAAA;AAAA;AAAA,CAAA;AAI3B,IAAM,UAAUA,uBAAA,CAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAQvB,IAAM,YAAYA,uBAAA,CAAO,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA;AAazB,IAAM,kBAAkBA,uBAAA,CAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAY/B,IAAM,gBAAgBA,uBAAA,CAAO,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAc7B,IAAM,UAAA,GAAa,CAAC,KAAA,KAA0C;AA7E9D,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA8EE,EAAA,MAAM,EAAC,IAAA,EAAM,UAAA,EAAU,GAAI,KAAA;AAC3B,EAAA,MAAM,EAAC,SAAO,GAAI,UAAA;AAUlB,EAAA,MAAM,OAAA,GAAA,CAAU,mCAAS,OAAA,KAAW,yBAAA;AACpC,EAAA,MAAM,iBAAiB,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,MAAA;AAGhC,EAAA,MAAM,oBAAoB,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,WAAA;AACnC,EAAA,MAAM,uBAAA,GAAA,CAA0B,EAAA,GAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,uBAAA,KAAT,IAAA,GAAA,EAAA,GAAoC,KAAA;AACpE,EAAA,MAAM,mBAAmB,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,gBAAA;AAElC,EAAA,MAAM,MAAA,GAASC,iBAAU,EAAC,UAAA,EAAA,CAAY,wCAAS,UAAA,KAAT,IAAA,GAAA,EAAA,GAAuB,cAAa,CAAA;AAC1E,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAIC,eAAiB,EAAE,CAAA;AAEjE,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,gBAAA,EAAkB;AACvB,IAAA,MAAA,CACG,KAAA,CAAc,gBAAgB,CAAA,CAC9B,IAAA,CAAK,CAAC,MAAA,KAAW;AAChB,MAAA,kBAAA,CAAmB,WAAW,IAAA,IAAQ,MAAA,KAAW,SAAY,EAAA,GAAK,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,IAClF,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,kBAAA,CAAmB,EAAE,CAAA;AAAA,IACvB,CAAC,CAAA;AAAA,EACL,CAAA,EAAG,CAAC,gBAAA,EAAkB,MAAM,CAAC,CAAA;AAC7B,EAAA,MAAM,SAASC,mBAAA,CAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA,IAAK;AAAA,IACxC,KAAA,EAAO,EAAA;AAAA,IACP,WAAA,EAAa,EAAA;AAAA,IACb,YAAA,EAAc;AAAA,GAChB;AACA,EAAA,MAAM,OAAA,GAEFA,mBAAA,CAAa,EAAE,CAAA,IAAK;AAAA,IACtB,IAAA,EAAM,EAAC,OAAA,EAAS,EAAA;AAAE,GACpB;AACA,EAAA,MAAM,IAAA,GAAA,CAAA,CAAe,EAAA,GAAA,OAAA,IAAA,IAAA,GAAA,MAAA,GAAA,OAAA,CAAS,IAAA,KAAT,IAAA,GAAA,MAAA,GAAA,EAAA,CAAe,OAAA,KAAW,EAAA;AAE/C,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,WAAA;AAAA,IACA,YAAA,EAAc;AAAA,GAChB,GAAI,MAAA;AAMJ,EAAA,MAAM,iBAAiB,MAAc;AACnC,IAAA,IAAI,kBAAkB,OAAO,eAAA;AAC7B,IAAA,IAAI,CAAC,mBAAmB,OAAO,EAAA;AAC/B,IAAA,IAAI,OAAO,sBAAsB,UAAA,EAAY;AAC3C,MAAA,OAAO,kBAAkB,OAAqD,CAAA;AAAA,IAChF;AACA,IAAA,OAAO,iBAAA;AAAA,EACT,CAAA;AACA,EAAA,MAAM,cAAsB,cAAA,EAAe;AAG3C,EAAA,MAAM,IAAA,GAAA,CAAQ,EAAA,GAAA,GAAA,IAAO,OAAA,KAAP,IAAA,GAAA,MAAA,GAAA,EAAA,CAAiB,QAAQ,MAAA,EAAQ,EAAA,CAAA;AAC/C,EAAA,MAAM,UAAU,MAAA,CAAO,IAAA,IAAQ,EAAE,CAAA,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACrD,EAAA,MAAM,IAAA,GAAO,MAAA;AAAA,IACX,cAAA,GAAiB,cAAA,CAAe,OAAqC,CAAA,GAAI;AAAA,GAC3E,CAAE,OAAA,CAAQ,YAAA,EAAc,EAAE,CAAA;AAC1B,EAAA,MAAM,OAAA,GAAU,CAAC,IAAA,EAAM,OAAO,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACxD,EAAA,MAAM,WAAW,OAAA,GAAU,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,GAAK,IAAA;AAGlD,EAAA,MAAM,UAAU,MAAM;AACpB,IAAA,IAAI;AACF,MAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,QAAA,IAAY,IAAI,CAAA;AAClC,MAAA,OAAO,CAAA,CAAE,QAAA;AAAA,IACX,CAAA,CAAA,OAAQ,CAAA,EAAA;AACN,MAAA,OAAO,aAAA;AAAA,IACT;AAAA,EACF,CAAA,GAAG;AAGH,EAAA,MAAM,aAAa,CAAA,EAAG,MAAM,CAAA,EAAG,OAAA,GAAU,WAAM,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA,CAAE,MAAM,EAAE,CAAA,CAAE,CAAC,CAAC,KAAK,EAAE,CAAA,CAAA;AAErF,EAAA,uBACEC,cAAA,CAACC,MAAA,EAAA,EAAI,OAAA,EAAS,CAAA,EACZ,0CAAC,gBAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAAC,eAAA,CAAC,aAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAAF,cAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO;AAAA,YACL,QAAA,EAAU,MAAA;AAAA,YACV,KAAA,EAAO,SAAA;AAAA,YACP,aAAA,EAAe,WAAA;AAAA,YACf,aAAA,EAAe;AAAA,WACjB;AAAA,UACD,QAAA,EAAA;AAAA;AAAA,OAED;AAAA,sCACC,aAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAAA,cAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO;AAAA,cACL,KAAA,EAAO,KAAA;AAAA,cACP,MAAA,EAAQ,KAAA;AAAA,cACR,YAAA,EAAc,KAAA;AAAA,cACd,eAAA,EAAiB,SAAA;AAAA,cACjB,OAAA,EAAS;AAAA;AACX;AAAA,SACF;AAAA,QAAE;AAAA,OAAA,EAEJ;AAAA,KAAA,EACF,CAAA;AAAA,oCAEC,WAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAAA,cAAA,CAAC,OAAA,EAAA,EAAS,QAAA,EAAA,QAAA,GAAW,UAAA,GAAa,6BAAA,EAAyB,CAAA;AAAA,qCAC1D,SAAA,EAAA,EACE,QAAA,EAAA,KAAA,IAAS,KAAA,CAAM,MAAA,GAAS,oBACvBE,eAAA,CAAAC,mBAAA,EAAA,EACG,QAAA,EAAA;AAAA,QAAAC,0BAAA,CAAS,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,EAAA,IAAM,cAAc,WAAA,CAAY,MAAA,GAAS,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA;AAAA,QAC5E,WAAA,oBACCF,eAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,OACE,uBAAA,GAA0B,MAAA,GAAY,EAAC,KAAA,EAAO,SAAA,EAAW,YAAY,GAAA,EAAG;AAAA,YAGzE,QAAA,EAAA;AAAA,cAAA,GAAA;AAAA,cAAI,IAAA;AAAA,cACF;AAAA;AAAA;AAAA;AACL,OAAA,EAEJ,IAEA,iCAAA,EAEJ,CAAA;AAAA,sBACAF,cAAA,CAAC,eAAA,EAAA,EACE,QAAA,EAAA,WAAA,IAAe,WAAA,CAAY,MAAA,GAAS,IACjCI,0BAAA,CAAS,WAAA,EAAa,GAAG,CAAA,GACzB,8DAAA,EACN;AAAA,KAAA,EACF;AAAA,GAAA,EACF,CAAA,EACF,CAAA;AAEJ,CAAA;AAEA,IAAO,kBAAA,GAAQ","file":"SeoPreview-WYH7NYNM.cjs","sourcesContent":["import {Box} from '@sanity/ui'\nimport {type ReactElement, useEffect, useState} from 'react'\nimport {StringInputProps, useClient, useFormValue} from 'sanity'\nimport styled from 'styled-components'\n\nimport {truncate} from '../utils/seoUtils'\n\nconst PreviewContainer = styled.div`\n max-width: 600px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: #ffffff;\n border: 1px solid #dadce0;\n border-radius: 8px;\n overflow: hidden;\n`\n\nconst PreviewHeader = styled.div`\n background: #f8f9fa;\n padding: 12px 16px;\n border-bottom: 1px solid #dadce0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n`\n\nconst PreviewBody = styled.div`\n padding: 16px;\n`\n\nconst SerpUrl = styled.p`\n margin: 0 0 4px;\n color: #006621;\n font-size: 13px;\n line-height: 1.4;\n word-break: break-word;\n`\n\nconst SerpTitle = styled.h3`\n margin: 0 0 8px;\n color: #1a0dab;\n font-size: 18px;\n font-weight: 500;\n line-height: 1.4;\n word-break: break-word;\n\n &:hover {\n text-decoration: underline;\n }\n`\n\nconst SerpDescription = styled.p`\n margin: 0;\n color: #545454;\n font-size: 14px;\n line-height: 1.6;\n word-break: break-word;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n`\n\nconst LiveIndicator = styled.span`\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: #4f46e5;\n background: #f0f4ff;\n padding: 4px 8px;\n border-radius: 4px;\n`\n\nconst SeoPreview = (props: StringInputProps): ReactElement => {\n const {path, schemaType} = props\n const {options} = schemaType as {\n options?: {\n baseUrl?: string\n apiVersion?: string\n prefix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffix?: ((doc: {_type?: string} & Record<string, unknown>) => string) | string\n titleSuffixInheritColor?: boolean\n titleSuffixQuery?: string\n }\n }\n const baseUrl = options?.baseUrl || 'https://www.example.com'\n const prefixFunction = options?.prefix as\n | ((doc: {_type?: string} & Record<string, unknown>) => string)\n | undefined\n const titleSuffixOption = options?.titleSuffix\n const titleSuffixInheritColor = options?.titleSuffixInheritColor ?? false\n const titleSuffixQuery = options?.titleSuffixQuery\n\n const client = useClient({apiVersion: options?.apiVersion ?? '2024-01-01'})\n const [groqTitleSuffix, setGroqTitleSuffix] = useState<string>('')\n\n useEffect(() => {\n if (!titleSuffixQuery) return\n client\n .fetch<string>(titleSuffixQuery)\n .then((result) => {\n setGroqTitleSuffix(result === null || result === undefined ? '' : String(result))\n })\n .catch(() => {\n setGroqTitleSuffix('')\n })\n }, [titleSuffixQuery, client])\n const parent = useFormValue([path[0]]) || {\n title: '',\n description: '',\n canonicalUrl: '',\n }\n const rootDoc: {\n slug?: {current: string}\n } = useFormValue([]) || {\n slug: {current: ''},\n }\n const slug: string = rootDoc?.slug?.current || ''\n\n const {\n title,\n description,\n canonicalUrl: url,\n } = parent as {\n title?: string\n description?: string\n canonicalUrl?: string\n }\n\n const getTitleSuffix = (): string => {\n if (titleSuffixQuery) return groqTitleSuffix\n if (!titleSuffixOption) return ''\n if (typeof titleSuffixOption === 'function') {\n return titleSuffixOption(rootDoc as {_type?: string} & Record<string, unknown>)\n }\n return titleSuffixOption\n }\n const titleSuffix: string = getTitleSuffix()\n\n // Build full URL\n const base = (url || baseUrl)?.replace(/\\/+$/, '')\n const slugStr = String(slug || '').replace(/^\\/+/, '')\n const pref = String(\n prefixFunction ? prefixFunction(rootDoc as {slug?: {current: string}}) : '',\n ).replace(/^\\/+|\\/+$/g, '')\n const urlPath = [pref, slugStr].filter(Boolean).join('/')\n const finalUrl = urlPath ? `${base}/${urlPath}` : base\n\n // Extract domain for display\n const domain = (() => {\n try {\n const u = new URL(finalUrl || base)\n return u.hostname\n } catch {\n return 'example.com'\n }\n })()\n\n // Format URL display with › separator\n const urlDisplay = `${domain}${urlPath ? ` › ${urlPath.split('/').slice(-1)[0]}` : ''}`\n\n return (\n <Box padding={3}>\n <PreviewContainer>\n <PreviewHeader>\n <span\n style={{\n fontSize: '11px',\n color: '#5f6368',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n }}\n >\n Search Preview\n </span>\n <LiveIndicator>\n <span\n style={{\n width: '4px',\n height: '4px',\n borderRadius: '50%',\n backgroundColor: '#4f46e5',\n display: 'inline-block',\n }}\n />\n Live\n </LiveIndicator>\n </PreviewHeader>\n\n <PreviewBody>\n <SerpUrl>{finalUrl ? urlDisplay : 'example.com › page-url'}</SerpUrl>\n <SerpTitle>\n {title && title.length > 0 ? (\n <>\n {truncate(title, Math.max(1, 60 - (titleSuffix ? titleSuffix.length + 3 : 0)))}\n {titleSuffix && (\n <span\n style={\n titleSuffixInheritColor ? undefined : {color: '#70757a', fontWeight: 400}\n }\n >\n {' '}\n | {titleSuffix}\n </span>\n )}\n </>\n ) : (\n 'Your SEO Title will appear here'\n )}\n </SerpTitle>\n <SerpDescription>\n {description && description.length > 0\n ? truncate(description, 160)\n : 'Your meta description will show up here. Make it compelling!'}\n </SerpDescription>\n </PreviewBody>\n </PreviewContainer>\n </Box>\n )\n}\n\nexport default SeoPreview\n"]}
@@ -21,26 +21,32 @@ var startsWithStopWord = (title) => {
21
21
  };
22
22
  var truncate = (text, maxLength) => text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
23
23
  var hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title);
24
- var getMetaTitleValidationMessages = (title, keywords, isParentseoField) => {
24
+ var getMetaTitleValidationMessages = (title, keywords, isParentseoField, suffixLength = 0) => {
25
25
  const feedback = [];
26
26
  const minChar = 50;
27
27
  const maxChar = 60;
28
28
  const charCount = (title == null ? void 0 : title.length) || 0;
29
+ const combinedLength = charCount + suffixLength;
30
+ const suffixMessage = suffixLength > 0 ? ` Suffix value is included (${suffixLength} chars).` : "";
29
31
  if (!(title == null ? void 0 : title.trim())) {
30
32
  feedback.push({ text: "Meta Title is empty. Add content to improve SEO.", color: "red" });
31
33
  return feedback;
32
34
  }
33
- if (charCount < minChar)
35
+ if (combinedLength < minChar)
34
36
  feedback.push({
35
- text: `Title is ${charCount} characters \u2014 below recommended ${minChar}.`,
37
+ text: `Title is ${combinedLength} characters \u2014 below recommended ${minChar}.${suffixMessage}`,
36
38
  color: "orange"
37
39
  });
38
- else if (charCount > maxChar)
40
+ else if (combinedLength > maxChar)
39
41
  feedback.push({
40
- text: `Title is ${charCount} characters \u2014 exceeds recommended ${maxChar}.`,
42
+ text: `Title is ${combinedLength} characters \u2014 exceeds recommended ${maxChar}.${suffixMessage}`,
41
43
  color: "red"
42
44
  });
43
- else feedback.push({ text: `Title length (${charCount}) looks good for SEO.`, color: "green" });
45
+ else
46
+ feedback.push({
47
+ text: `Title length (${combinedLength}) looks good for SEO.${suffixMessage}`,
48
+ color: "green"
49
+ });
44
50
  if (isParentseoField) {
45
51
  if (keywords.length > 0) {
46
52
  const hasKeyword = hasMatchingKeyword(title, keywords);
@@ -468,5 +474,5 @@ var getTwitterImageUrlValidation = (imageUrl, seoParent) => {
468
474
  };
469
475
 
470
476
  export { getMetaDescriptionValidationMessages, getMetaImageValidation, getMetaTitleValidationMessages, getOgDescriptionValidation, getOgImageUrlValidation, getOgImageValidation, getOgTitleValidation, getTwitterDescriptionValidation, getTwitterImageUrlValidation, getTwitterImageValidation, getTwitterTitleValidation, truncate };
471
- //# sourceMappingURL=chunk-25JLWVEU.js.map
472
- //# sourceMappingURL=chunk-25JLWVEU.js.map
477
+ //# sourceMappingURL=chunk-HDZZQCH7.js.map
478
+ //# sourceMappingURL=chunk-HDZZQCH7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/seoUtils.ts"],"names":[],"mappings":"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","file":"chunk-HDZZQCH7.js","sourcesContent":["import {FeedbackType} from '../types'\n\nexport const stopWords = ['the', 'a', 'an', 'and', 'or', 'but']\n\nexport const hasMatchingKeyword = (title: string, keywordList: string[]): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()))\n}\n\nexport const hasKeywordOveruse = (\n title: string,\n keywordList: string[],\n maxOccurrences = 3,\n): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => {\n if (!keyword) return false\n const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), 'g'))\n return matches ? matches.length > maxOccurrences : false\n })\n}\n\nexport const startsWithStopWord = (title: string): boolean => {\n if (!title) return false\n const firstWord = title.trim().split(' ')[0].toLowerCase()\n return stopWords.includes(firstWord)\n}\n\nexport const primaryKeywordAtStart = (title: string, keywords: string[]): boolean => {\n if (!title || keywords.length === 0) return true\n return title.toLowerCase().startsWith(keywords[0].toLowerCase())\n}\n\nexport const truncate = (text: string, maxLength: number): string =>\n text.length > maxLength ? `${text.slice(0, maxLength)}…` : text\n\nexport const hasExcessivePunctuation = (title: string): boolean => /[!@#$%^&*]{2,}/.test(title)\n\nexport const getMetaTitleValidationMessages = (\n title: string,\n keywords: string[],\n isParentseoField: boolean,\n suffixLength = 0,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n const minChar = 50\n const maxChar = 60\n const charCount = title?.length || 0\n const combinedLength = charCount + suffixLength\n const suffixMessage = suffixLength > 0 ? ` Suffix value is included (${suffixLength} chars).` : ''\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'Meta Title is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check (evaluated against combined title + suffix length)\n if (combinedLength < minChar)\n feedback.push({\n text: `Title is ${combinedLength} characters — below recommended ${minChar}.${suffixMessage}`,\n color: 'orange',\n })\n else if (combinedLength > maxChar)\n feedback.push({\n text: `Title is ${combinedLength} characters — exceeds recommended ${maxChar}.${suffixMessage}`,\n color: 'red',\n })\n else\n feedback.push({\n text: `Title length (${combinedLength}) looks good for SEO.${suffixMessage}`,\n color: 'green',\n })\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in title — good job!'\n : 'Keywords defined but missing in title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word check\n if (startsWithStopWord(title))\n feedback.push({text: 'Title starts with a stop word — consider rephrasing.', color: 'orange'})\n\n // Punctuation check\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getMetaDescriptionValidationMessages = (\n description: string,\n keywords: string[],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n const minChar = 120\n const maxChar = 160\n const charCount = description?.length || 0\n\n if (!description?.trim()) {\n feedback.push({text: 'Meta description is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Description is ${charCount} chars — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Description is ${charCount} chars — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else\n feedback.push({text: `Description length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(description, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in description — good job!'\n : 'Keywords defined but missing in description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(description, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word / filler check\n if (startsWithStopWord(description))\n feedback.push({\n text: 'Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n // Punctuation / special characters\n if (hasExcessivePunctuation(description))\n feedback.push({\n text: 'Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getOgTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n const min = 40\n const max = 60\n const count = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'OG Title is empty. Add content for better social preview.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({text: `OG Title is ${count} chars — exceeds recommended ${max}.`, color: 'red'})\n else feedback.push({text: `OG Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG title — good job!'\n : 'Keywords defined but missing in OG title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG title — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(title))\n feedback.push({\n text: 'OG Title starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'OG Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getOgDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n const min = 90\n const max = 120\n const count = desc?.length || 0\n\n // Empty check\n if (!desc?.trim()) {\n feedback.push({\n text: 'OG Description is empty. Add content for better social preview.',\n color: 'red',\n })\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `OG Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `OG Description length (${count}) looks good.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG description — good job!'\n : 'Keywords defined but missing in OG description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(desc, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG description — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(desc))\n feedback.push({\n text: 'OG Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(desc))\n feedback.push({\n text: 'OG Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getTwitterTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n const min = 30\n const max = 70\n const count = title?.length || 0\n\n if (!title?.trim()) {\n feedback.push({text: 'X Title is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `X Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `X Title is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `X Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in X title — good job!'\n : 'Keywords defined but missing in X title.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(title))\n feedback.push({text: 'X Title has excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getTwitterDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n const min = 50\n const max = 200\n const count = desc?.length || 0\n\n if (!desc?.trim()) {\n feedback.push({text: 'X Description is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `X Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `X Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `X Description length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in X description — good job!'\n : 'Keywords defined but missing in X description.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(desc))\n feedback.push({\n text: 'X Description has excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\n// ── Image Validation Helpers ──\n\n/** Check if an image is set in an OG/Twitter sub-object (handles upload vs URL) */\nexport const isSubImageSet = (subObj: Record<string, unknown> | null | undefined): boolean => {\n if (!subObj) return false\n if (subObj.imageType === 'url') return !!(subObj.imageUrl as string)?.trim()\n const img = subObj.image as Record<string, unknown> | undefined\n return !!img?.asset\n}\n\n/** Check if metaImage asset is set */\nconst isMetaImageSet = (seoParent: Record<string, unknown> | null | undefined): boolean => {\n if (!seoParent) return false\n const metaImage = seoParent.metaImage as Record<string, unknown> | undefined\n return !!metaImage?.asset\n}\n\nexport const getMetaImageValidation = (\n hasImage: boolean,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!hasImage) {\n feedback.push({\n text: 'No meta image provided. Adding an image improves click-through rates.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'Meta image is set — great for SEO and social sharing.', color: 'green'})\n\n const ogSet = isSubImageSet(seoParent?.openGraph as Record<string, unknown> | undefined)\n const twSet = isSubImageSet(seoParent?.twitter as Record<string, unknown> | undefined)\n\n if (!ogSet && !twSet) {\n feedback.push({\n text: 'OG and Twitter images are missing — add them for full social coverage.',\n color: 'orange',\n })\n } else if (!ogSet) {\n feedback.push({\n text: 'OG image is missing — add it for better Facebook/LinkedIn previews.',\n color: 'orange',\n })\n } else if (twSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n feedback.push({\n text: 'Twitter image is missing — add it for better X (Twitter) cards.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n\nexport const getOgImageValidation = (\n hasImage: boolean,\n altText: string | undefined,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!hasImage) {\n feedback.push({\n text: 'No OG image provided. Social shares will lack a visual preview.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'OG image is set — good for social sharing.', color: 'green'})\n\n if (altText?.trim()) {\n feedback.push({text: 'Alt text is set — good for accessibility.', color: 'green'})\n } else {\n feedback.push({text: 'Consider adding alt text for better accessibility.', color: 'orange'})\n }\n\n const metaSet = isMetaImageSet(seoParent)\n const twSet = isSubImageSet(seoParent?.twitter as Record<string, unknown> | undefined)\n\n if (metaSet && twSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n if (!metaSet)\n feedback.push({\n text: 'Meta image is missing — add it for search engine results.',\n color: 'orange',\n })\n if (!twSet)\n feedback.push({\n text: 'Twitter image is missing — add it for X (Twitter) cards.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n\nexport const getOgImageUrlValidation = (\n imageUrl: string | undefined,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!imageUrl?.trim()) {\n feedback.push({\n text: 'No OG image URL provided. Social shares will lack a visual preview.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'OG image URL is set — good for social sharing.', color: 'green'})\n\n const metaSet = isMetaImageSet(seoParent)\n const twSet = isSubImageSet(seoParent?.twitter as Record<string, unknown> | undefined)\n\n if (metaSet && twSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n if (!metaSet)\n feedback.push({\n text: 'Meta image is missing — add it for search engine results.',\n color: 'orange',\n })\n if (!twSet)\n feedback.push({\n text: 'Twitter image is missing — add it for X (Twitter) cards.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n\nexport const getTwitterImageValidation = (\n hasImage: boolean,\n altText: string | undefined,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!hasImage) {\n feedback.push({\n text: 'No Twitter image provided. Posts on X will lack a visual.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'Twitter image is set — good for X sharing.', color: 'green'})\n\n if (altText?.trim()) {\n feedback.push({text: 'Alt text is set — good for accessibility.', color: 'green'})\n } else {\n feedback.push({text: 'Consider adding alt text for better accessibility.', color: 'orange'})\n }\n\n const metaSet = isMetaImageSet(seoParent)\n const ogSet = isSubImageSet(seoParent?.openGraph as Record<string, unknown> | undefined)\n\n if (metaSet && ogSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n if (!metaSet)\n feedback.push({\n text: 'Meta image is missing — add it for search engine results.',\n color: 'orange',\n })\n if (!ogSet)\n feedback.push({\n text: 'OG image is missing — add it for Facebook/LinkedIn sharing.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n\nexport const getTwitterImageUrlValidation = (\n imageUrl: string | undefined,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!imageUrl?.trim()) {\n feedback.push({\n text: 'No Twitter image URL provided. Posts on X will lack a visual.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'Twitter image URL is set — good for X sharing.', color: 'green'})\n\n const metaSet = isMetaImageSet(seoParent)\n const ogSet = isSubImageSet(seoParent?.openGraph as Record<string, unknown> | undefined)\n\n if (metaSet && ogSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n if (!metaSet)\n feedback.push({\n text: 'Meta image is missing — add it for search engine results.',\n color: 'orange',\n })\n if (!ogSet)\n feedback.push({\n text: 'OG image is missing — add it for Facebook/LinkedIn sharing.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n"]}
@@ -23,26 +23,32 @@ var startsWithStopWord = (title) => {
23
23
  };
24
24
  var truncate = (text, maxLength) => text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
25
25
  var hasExcessivePunctuation = (title) => /[!@#$%^&*]{2,}/.test(title);
26
- var getMetaTitleValidationMessages = (title, keywords, isParentseoField) => {
26
+ var getMetaTitleValidationMessages = (title, keywords, isParentseoField, suffixLength = 0) => {
27
27
  const feedback = [];
28
28
  const minChar = 50;
29
29
  const maxChar = 60;
30
30
  const charCount = (title == null ? void 0 : title.length) || 0;
31
+ const combinedLength = charCount + suffixLength;
32
+ const suffixMessage = suffixLength > 0 ? ` Suffix value is included (${suffixLength} chars).` : "";
31
33
  if (!(title == null ? void 0 : title.trim())) {
32
34
  feedback.push({ text: "Meta Title is empty. Add content to improve SEO.", color: "red" });
33
35
  return feedback;
34
36
  }
35
- if (charCount < minChar)
37
+ if (combinedLength < minChar)
36
38
  feedback.push({
37
- text: `Title is ${charCount} characters \u2014 below recommended ${minChar}.`,
39
+ text: `Title is ${combinedLength} characters \u2014 below recommended ${minChar}.${suffixMessage}`,
38
40
  color: "orange"
39
41
  });
40
- else if (charCount > maxChar)
42
+ else if (combinedLength > maxChar)
41
43
  feedback.push({
42
- text: `Title is ${charCount} characters \u2014 exceeds recommended ${maxChar}.`,
44
+ text: `Title is ${combinedLength} characters \u2014 exceeds recommended ${maxChar}.${suffixMessage}`,
43
45
  color: "red"
44
46
  });
45
- else feedback.push({ text: `Title length (${charCount}) looks good for SEO.`, color: "green" });
47
+ else
48
+ feedback.push({
49
+ text: `Title length (${combinedLength}) looks good for SEO.${suffixMessage}`,
50
+ color: "green"
51
+ });
46
52
  if (isParentseoField) {
47
53
  if (keywords.length > 0) {
48
54
  const hasKeyword = hasMatchingKeyword(title, keywords);
@@ -481,5 +487,5 @@ exports.getTwitterImageUrlValidation = getTwitterImageUrlValidation;
481
487
  exports.getTwitterImageValidation = getTwitterImageValidation;
482
488
  exports.getTwitterTitleValidation = getTwitterTitleValidation;
483
489
  exports.truncate = truncate;
484
- //# sourceMappingURL=chunk-IFDLKZET.cjs.map
485
- //# sourceMappingURL=chunk-IFDLKZET.cjs.map
490
+ //# sourceMappingURL=chunk-ZBHLMQTS.cjs.map
491
+ //# sourceMappingURL=chunk-ZBHLMQTS.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/seoUtils.ts"],"names":[],"mappings":"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","file":"chunk-ZBHLMQTS.cjs","sourcesContent":["import {FeedbackType} from '../types'\n\nexport const stopWords = ['the', 'a', 'an', 'and', 'or', 'but']\n\nexport const hasMatchingKeyword = (title: string, keywordList: string[]): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => keyword && lowerTitle.includes(keyword.toLowerCase()))\n}\n\nexport const hasKeywordOveruse = (\n title: string,\n keywordList: string[],\n maxOccurrences = 3,\n): boolean => {\n if (!title || keywordList.length === 0) return false\n const lowerTitle = title.toLowerCase()\n return keywordList.some((keyword) => {\n if (!keyword) return false\n const matches = lowerTitle.match(new RegExp(keyword.toLowerCase(), 'g'))\n return matches ? matches.length > maxOccurrences : false\n })\n}\n\nexport const startsWithStopWord = (title: string): boolean => {\n if (!title) return false\n const firstWord = title.trim().split(' ')[0].toLowerCase()\n return stopWords.includes(firstWord)\n}\n\nexport const primaryKeywordAtStart = (title: string, keywords: string[]): boolean => {\n if (!title || keywords.length === 0) return true\n return title.toLowerCase().startsWith(keywords[0].toLowerCase())\n}\n\nexport const truncate = (text: string, maxLength: number): string =>\n text.length > maxLength ? `${text.slice(0, maxLength)}…` : text\n\nexport const hasExcessivePunctuation = (title: string): boolean => /[!@#$%^&*]{2,}/.test(title)\n\nexport const getMetaTitleValidationMessages = (\n title: string,\n keywords: string[],\n isParentseoField: boolean,\n suffixLength = 0,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n const minChar = 50\n const maxChar = 60\n const charCount = title?.length || 0\n const combinedLength = charCount + suffixLength\n const suffixMessage = suffixLength > 0 ? ` Suffix value is included (${suffixLength} chars).` : ''\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'Meta Title is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check (evaluated against combined title + suffix length)\n if (combinedLength < minChar)\n feedback.push({\n text: `Title is ${combinedLength} characters — below recommended ${minChar}.${suffixMessage}`,\n color: 'orange',\n })\n else if (combinedLength > maxChar)\n feedback.push({\n text: `Title is ${combinedLength} characters — exceeds recommended ${maxChar}.${suffixMessage}`,\n color: 'red',\n })\n else\n feedback.push({\n text: `Title length (${combinedLength}) looks good for SEO.${suffixMessage}`,\n color: 'green',\n })\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in title — good job!'\n : 'Keywords defined but missing in title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word check\n if (startsWithStopWord(title))\n feedback.push({text: 'Title starts with a stop word — consider rephrasing.', color: 'orange'})\n\n // Punctuation check\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getMetaDescriptionValidationMessages = (\n description: string,\n keywords: string[],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n const minChar = 120\n const maxChar = 160\n const charCount = description?.length || 0\n\n if (!description?.trim()) {\n feedback.push({text: 'Meta description is empty. Add content to improve SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (charCount < minChar)\n feedback.push({\n text: `Description is ${charCount} chars — below recommended ${minChar}.`,\n color: 'orange',\n })\n else if (charCount > maxChar)\n feedback.push({\n text: `Description is ${charCount} chars — exceeds recommended ${maxChar}.`,\n color: 'red',\n })\n else\n feedback.push({text: `Description length (${charCount}) looks good for SEO.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(description, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in description — good job!'\n : 'Keywords defined but missing in description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(description, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Stop word / filler check\n if (startsWithStopWord(description))\n feedback.push({\n text: 'Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n // Punctuation / special characters\n if (hasExcessivePunctuation(description))\n feedback.push({\n text: 'Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getOgTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n const min = 40\n const max = 60\n const count = title?.length || 0\n\n // Empty check\n if (!title?.trim()) {\n feedback.push({text: 'OG Title is empty. Add content for better social preview.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({text: `OG Title is ${count} chars — exceeds recommended ${max}.`, color: 'red'})\n else feedback.push({text: `OG Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG title — good job!'\n : 'Keywords defined but missing in OG title.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(title, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG title — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(title))\n feedback.push({\n text: 'OG Title starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(title))\n feedback.push({text: 'OG Title contains excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getOgDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n const min = 90\n const max = 120\n const count = desc?.length || 0\n\n // Empty check\n if (!desc?.trim()) {\n feedback.push({\n text: 'OG Description is empty. Add content for better social preview.',\n color: 'red',\n })\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `OG Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `OG Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `OG Description length (${count}) looks good.`, color: 'green'})\n\n // Keyword checks\n if (isParentseoField) {\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in OG description — good job!'\n : 'Keywords defined but missing in OG description.',\n color: hasKeyword ? 'green' : 'red',\n })\n\n if (hasKeywordOveruse(desc, keywords)) {\n feedback.push({\n text: 'Keyword appears too many times in OG description — avoid keyword stuffing.',\n color: 'orange',\n })\n }\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Additional OG-specific checks\n if (startsWithStopWord(desc))\n feedback.push({\n text: 'OG Description starts with a stop word — consider rephrasing.',\n color: 'orange',\n })\n\n if (hasExcessivePunctuation(desc))\n feedback.push({\n text: 'OG Description contains excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\nexport const getTwitterTitleValidation = (\n title: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n const min = 30\n const max = 70\n const count = title?.length || 0\n\n if (!title?.trim()) {\n feedback.push({text: 'X Title is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `X Title is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `X Title is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `X Title length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(title, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in X title — good job!'\n : 'Keywords defined but missing in X title.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(title))\n feedback.push({text: 'X Title has excessive punctuation — simplify it.', color: 'orange'})\n\n return feedback\n}\n\nexport const getTwitterDescriptionValidation = (\n desc: string,\n keywords: string[] = [],\n isParentseoField: boolean,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n const min = 50\n const max = 200\n const count = desc?.length || 0\n\n if (!desc?.trim()) {\n feedback.push({text: 'X Description is empty. Add content for better SEO.', color: 'red'})\n return feedback\n }\n\n // Length check\n if (count < min)\n feedback.push({\n text: `X Description is ${count} chars — shorter than recommended ${min}.`,\n color: 'orange',\n })\n else if (count > max)\n feedback.push({\n text: `X Description is ${count} chars — exceeds recommended ${max}.`,\n color: 'red',\n })\n else feedback.push({text: `X Description length (${count}) looks good.`, color: 'green'})\n\n if (isParentseoField) {\n // Keyword checks\n if (keywords.length > 0) {\n const hasKeyword = hasMatchingKeyword(desc, keywords)\n feedback.push({\n text: hasKeyword\n ? 'Keyword found in X description — good job!'\n : 'Keywords defined but missing in X description.',\n color: hasKeyword ? 'green' : 'red',\n })\n } else {\n feedback.push({\n text: 'No keywords defined. Consider adding relevant keywords.',\n color: 'orange',\n })\n }\n }\n\n // Punctuation check\n if (/[!@#$%^&*]{2,}/.test(desc))\n feedback.push({\n text: 'X Description has excessive punctuation — simplify it.',\n color: 'orange',\n })\n\n return feedback\n}\n\n// ── Image Validation Helpers ──\n\n/** Check if an image is set in an OG/Twitter sub-object (handles upload vs URL) */\nexport const isSubImageSet = (subObj: Record<string, unknown> | null | undefined): boolean => {\n if (!subObj) return false\n if (subObj.imageType === 'url') return !!(subObj.imageUrl as string)?.trim()\n const img = subObj.image as Record<string, unknown> | undefined\n return !!img?.asset\n}\n\n/** Check if metaImage asset is set */\nconst isMetaImageSet = (seoParent: Record<string, unknown> | null | undefined): boolean => {\n if (!seoParent) return false\n const metaImage = seoParent.metaImage as Record<string, unknown> | undefined\n return !!metaImage?.asset\n}\n\nexport const getMetaImageValidation = (\n hasImage: boolean,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!hasImage) {\n feedback.push({\n text: 'No meta image provided. Adding an image improves click-through rates.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'Meta image is set — great for SEO and social sharing.', color: 'green'})\n\n const ogSet = isSubImageSet(seoParent?.openGraph as Record<string, unknown> | undefined)\n const twSet = isSubImageSet(seoParent?.twitter as Record<string, unknown> | undefined)\n\n if (!ogSet && !twSet) {\n feedback.push({\n text: 'OG and Twitter images are missing — add them for full social coverage.',\n color: 'orange',\n })\n } else if (!ogSet) {\n feedback.push({\n text: 'OG image is missing — add it for better Facebook/LinkedIn previews.',\n color: 'orange',\n })\n } else if (twSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n feedback.push({\n text: 'Twitter image is missing — add it for better X (Twitter) cards.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n\nexport const getOgImageValidation = (\n hasImage: boolean,\n altText: string | undefined,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!hasImage) {\n feedback.push({\n text: 'No OG image provided. Social shares will lack a visual preview.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'OG image is set — good for social sharing.', color: 'green'})\n\n if (altText?.trim()) {\n feedback.push({text: 'Alt text is set — good for accessibility.', color: 'green'})\n } else {\n feedback.push({text: 'Consider adding alt text for better accessibility.', color: 'orange'})\n }\n\n const metaSet = isMetaImageSet(seoParent)\n const twSet = isSubImageSet(seoParent?.twitter as Record<string, unknown> | undefined)\n\n if (metaSet && twSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n if (!metaSet)\n feedback.push({\n text: 'Meta image is missing — add it for search engine results.',\n color: 'orange',\n })\n if (!twSet)\n feedback.push({\n text: 'Twitter image is missing — add it for X (Twitter) cards.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n\nexport const getOgImageUrlValidation = (\n imageUrl: string | undefined,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!imageUrl?.trim()) {\n feedback.push({\n text: 'No OG image URL provided. Social shares will lack a visual preview.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'OG image URL is set — good for social sharing.', color: 'green'})\n\n const metaSet = isMetaImageSet(seoParent)\n const twSet = isSubImageSet(seoParent?.twitter as Record<string, unknown> | undefined)\n\n if (metaSet && twSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n if (!metaSet)\n feedback.push({\n text: 'Meta image is missing — add it for search engine results.',\n color: 'orange',\n })\n if (!twSet)\n feedback.push({\n text: 'Twitter image is missing — add it for X (Twitter) cards.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n\nexport const getTwitterImageValidation = (\n hasImage: boolean,\n altText: string | undefined,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!hasImage) {\n feedback.push({\n text: 'No Twitter image provided. Posts on X will lack a visual.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'Twitter image is set — good for X sharing.', color: 'green'})\n\n if (altText?.trim()) {\n feedback.push({text: 'Alt text is set — good for accessibility.', color: 'green'})\n } else {\n feedback.push({text: 'Consider adding alt text for better accessibility.', color: 'orange'})\n }\n\n const metaSet = isMetaImageSet(seoParent)\n const ogSet = isSubImageSet(seoParent?.openGraph as Record<string, unknown> | undefined)\n\n if (metaSet && ogSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n if (!metaSet)\n feedback.push({\n text: 'Meta image is missing — add it for search engine results.',\n color: 'orange',\n })\n if (!ogSet)\n feedback.push({\n text: 'OG image is missing — add it for Facebook/LinkedIn sharing.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n\nexport const getTwitterImageUrlValidation = (\n imageUrl: string | undefined,\n seoParent: Record<string, unknown> | null | undefined,\n): FeedbackType[] => {\n const feedback: FeedbackType[] = []\n\n if (!imageUrl?.trim()) {\n feedback.push({\n text: 'No Twitter image URL provided. Posts on X will lack a visual.',\n color: 'red',\n })\n return feedback\n }\n\n feedback.push({text: 'Twitter image URL is set — good for X sharing.', color: 'green'})\n\n const metaSet = isMetaImageSet(seoParent)\n const ogSet = isSubImageSet(seoParent?.openGraph as Record<string, unknown> | undefined)\n\n if (metaSet && ogSet) {\n feedback.push({text: 'All images set (Meta, OG, Twitter) — full coverage!', color: 'green'})\n } else {\n if (!metaSet)\n feedback.push({\n text: 'Meta image is missing — add it for search engine results.',\n color: 'orange',\n })\n if (!ogSet)\n feedback.push({\n text: 'OG image is missing — add it for Facebook/LinkedIn sharing.',\n color: 'orange',\n })\n }\n\n return feedback\n}\n"]}
package/dist/cli.js CHANGED
@@ -61,7 +61,7 @@ ${e}`}function ft(e,t,n,o){a.success(`Updated ${e}`),a.dim(""),a.dim(" What was
61
61
  title,
62
62
  slug,
63
63
  seo
64
- } | order(_updatedAt desc)`;o.text="Fetching documents...";let c;try{c=await s.fetch(d);}catch(g){o.fail(`Failed to fetch: ${g.message}`),process.exit(1);}if(o.succeed(`Analysed ${c.length} document(s)`),c.length===0){a.warn("No documents with SEO fields found."),n&&Se(i);return}let u=c.map(g=>({doc:g,health:K(g)}));await gt(u,c,e),n&&Se(i);}async function gt(e,t,n){let o={excellent:0,good:0,fair:0,poor:0,missing:0},s=0;for(let{health:c}of e)o[c.status]=(o[c.status]||0)+1,s+=c.score;let i=Math.round(s/e.length);if(pt(t,i,o),n.format==="summary")return;n.format!=="table"&&(a.info(""),a.warn(`Unknown format "${n.format}", defaulting to table output.`)),St(e);let r={};for(let{health:c}of e)for(let u of c.issues)r[u]=(r[u]||0)+1;let d=Object.entries(r).sort(([,c],[,u])=>u-c).slice(0,10);d.length>0&&$t(d,t.length),a.info("");}function pt(e,t,n){a.heading("\u{1F4CA} Summary"),a.info(""),a.info(` Total documents: ${l.bold(String(e.length))}`),a.info(` Average score: ${l.bold(we(t))}%`),a.info(""),a.info(` ${A.excellent} Excellent (80+): ${n.excellent}`),a.info(` ${A.good} Good (60-79): ${n.good}`),a.info(` ${A.fair} Fair (40-59): ${n.fair}`),a.info(` ${A.poor} Poor (1-39): ${n.poor}`),a.info(` ${A.missing} Missing (0): ${n.missing}`);}var mt={excellent:"Excellent",good:"Good",fair:"Fair",poor:"Poor",missing:"Missing"},ht={excellent:l.green,good:l.blue,fair:l.yellow,poor:l.red,missing:l.dim};function yt(e,t=11){var s,i;let n=((s=mt[e])!=null?s:e).padEnd(t);return ((i=ht[e])!=null?i:(r=>r))(n)}function St(e){a.heading("\u{1F4CB} Documents"),a.info("");let t=Math.min(44,Math.max(5,...e.map(({doc:r})=>(r.title||r._id).length))),n=11,o=14,s=5,i=l.dim("\u2500".repeat(s+2+n+2+o+2+t));a.info(` ${l.dim("Score".padEnd(s))} ${l.dim("Status".padEnd(n))} ${l.dim("Type".padEnd(o))} ${l.dim("Title")}`),a.info(` ${i}`);for(let{doc:r,health:d}of e){let c=$e(r.title||r._id,t),u=String(d.score).padStart(s),f=yt(d.status,n),g=l.dim($e(r._type,o).padEnd(o));a.info(` ${we(d.score,u)} ${f} ${g} ${c}`);}}function $t(e,t){a.heading("\u{1F50D} Top Issues"),a.info("");for(let[n,o]of e){let s=Math.round(o/t*100);a.info(` ${l.red(String(o).padStart(4))} docs (${s}%) ${n}`);}}function Se(e){a.info(""),a.info(` ${l.dim("\u2500".repeat(52))}`),a.info(` ${l.dim("Connection info")}`),a.info(` ${l.dim("Project ID:")} ${l.white(e.projectId)} ${l.dim(`\u2190 ${e.sources.projectId}`)}`),a.info(` ${l.dim("Dataset: ")} ${l.white(e.dataset)} ${l.dim(`\u2190 ${e.sources.dataset}`)}`),a.info(` ${l.dim("Token: ")} ${l.dim(e.sources.token)}`),a.info("");}function we(e,t){let n=t!=null?t:String(e);return e>=80?l.green(n):e>=60?l.blue(n):e>=40?l.yellow(n):e>0?l.red(n):l.dim(n)}function $e(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e}H();var Z="1.6.4",ve=[l.cyan,e=>l.bold(l.blue(e)),l.blue,l.magenta,l.red,l.yellow],w=ve[Math.floor(Math.random()*ve.length)],Ce=["Run `seofields doctor` first to verify your setup is correct.","Use `seofields report --format summary` for a quick overview.","Export your SEO data before making bulk changes: `seofields export -o backup.json`.","Add `seoFields` type to every document schema to track SEO health.","A meta title between 50\u201360 characters gets the best click-through rate.","Meta descriptions should be 140\u2013160 characters for optimal display.","Open Graph images should be 1200\xD7630px for best social sharing.","Use `seofields config --baseUrl=https://yoursite.com` to enable canonical URLs.","Twitter Card images require a separate `twitterImage` field for best results.","Schema.org markup helps search engines understand your content structure.","Use `seofields report --types post,page` to filter by document type.","The `healthDashboard` tool gives a live view of SEO scores inside Sanity Studio.","Keywords should appear naturally \u2014 avoid keyword stuffing.","Every page needs a unique meta title. Duplicates hurt rankings.","Use `seofields export --format csv` to open SEO data in a spreadsheet.","Set `noIndex: true` on internal or draft pages to prevent indexing.","OG title and meta title can differ \u2014 OG is optimised for social, meta for search.","Run `seofields report` after content migrations to catch regressions.","Use `seofields init --schema-org` to add structured data support.","Structured data (Schema.org) can unlock rich results in Google Search.","A score of 80+ is excellent. Aim to get all key pages there.","The `seoPreview` option shows a live Google-style preview inside the editor.","Use `seofields report` in CI to catch SEO regressions before they ship.","Use `--dataset staging` to run reports against your staging dataset.","Keep your license key private \u2014 never commit it to version control.","Use `seofields config --healthDashboard.showDocumentId=true` for easier debugging.","Use `seofields export --format json --output report.json` to save SEO data to a file.","Alt text on images improves both accessibility and image search ranking.","Canonical URLs prevent duplicate content penalties from search engines.","A missing OG image means social shares show a blank card \u2014 always add one.","Use descriptive slugs: `/blog/seo-tips` ranks better than `/blog/post-123`.","Re-run `seofields report` monthly to track your SEO health over time.","The `types` filter helps you focus reports on high-priority content.","Use `seofields export` to back up SEO data before schema migrations.","Short meta titles (under 30 chars) waste valuable search result space.","Long meta descriptions get truncated \u2014 stay under 160 characters.","Twitter Cards need `twitter:card`, `twitter:title`, and `twitter:image` at minimum.","Use `seofields doctor` after upgrading the plugin to catch breaking changes.","JSON export includes health scores \u2014 pipe it into your analytics pipeline.","Set a `defaultHiddenFields` list to keep the SEO panel focused.","Internal links pass authority \u2014 make sure important pages are well-linked.","Page speed is a ranking factor \u2014 optimise images referenced in SEO fields.","Use consistent brand phrasing in meta titles across your site.","Structured data errors can be tested at schema.org/validator.","Focus first on pages with the most traffic, not just the lowest scores.","Add SEO review to your content publishing checklist.","Use `seofields config --healthDashboard.licenseKey=KEY` to unlock premium features.","The CSV export makes it easy to share SEO audits with non-technical stakeholders.","Every image on your page should have descriptive alt text.","Avoid duplicate meta descriptions \u2014 each page needs its own."];function bt(){return Ce[Math.floor(Math.random()*Ce.length)]}var wt=62,V=wt-2;function vt(e,t){let n=e.split(" "),o=[],s="";for(let i of n)s.length+(s?1:0)+i.length>t?(s&&o.push(s),s=i):s=s?`${s} ${i}`:i;return s&&o.push(s),o}function F(e,t){let n=V-3,o=" ".repeat(Math.max(0,n-t));return ` ${w("\u2502")} ${e}${o} ${w("\u2502")}`}function M(){return F("",0)}function xe(){let e="\u2500".repeat(V),t=bt(),n=V-3-5,o=vt(t,n);console.log(),console.log(` ${w(`\u256D${e}\u256E`)}`),console.log(M()),console.log(F(`${l.bold(l.green("seofields"))}${l.dim(` v${Z}`)}`,`seofields v${Z}`.length)),console.log(F(l.dim("SEO tooling for Sanity CMS \u2014 manage from your terminal"),54)),console.log(M());for(let i=0;i<o.length;i++){let r=i===0?`${l.dim("Tip: ")}${l.white(o[i])}`:` ${l.white(o[i])}`,d=5+o[i].length;console.log(F(r,d));}console.log(M()),console.log(` ${w(`\u2570${e}\u256F`)}`),console.log(),console.log(` ${l.bold("Commands")}`),console.log();let s=[["init ","Add seofields() to sanity.config"],["config ","Update plugin configuration options"],["create-config ","Create seofields.cli.ts / .js interactively"],["report ","SEO health report across all documents"],["export ","Export SEO fields as JSON or CSV"],["doctor ","Check setup, deps & config"]];for(let[i,r]of s)console.log(` ${w(i)} ${l.dim(r)}`);console.log(),console.log(` ${l.dim("Run ")}${l.white("seofields <command> --help")}${l.dim(" for details")}`),console.log();}function P(e,t){let n="\u2500".repeat(V),o=`seofields \u203A ${e}`;return ["",` ${w(`\u256D${n}\u256E`)}`,M(),F(`${l.bold(l.green("seofields"))}${w(" \u203A ")}${l.bold(w(e))}`,o.length),F(l.dim(t),t.length),M(),` ${w(`\u2570${n}\u256F`)}`].join(`
64
+ } | order(_updatedAt desc)`;o.text="Fetching documents...";let c;try{c=await s.fetch(d);}catch(g){o.fail(`Failed to fetch: ${g.message}`),process.exit(1);}if(o.succeed(`Analysed ${c.length} document(s)`),c.length===0){a.warn("No documents with SEO fields found."),n&&Se(i);return}let u=c.map(g=>({doc:g,health:K(g)}));await gt(u,c,e),n&&Se(i);}async function gt(e,t,n){let o={excellent:0,good:0,fair:0,poor:0,missing:0},s=0;for(let{health:c}of e)o[c.status]=(o[c.status]||0)+1,s+=c.score;let i=Math.round(s/e.length);if(pt(t,i,o),n.format==="summary")return;n.format!=="table"&&(a.info(""),a.warn(`Unknown format "${n.format}", defaulting to table output.`)),St(e);let r={};for(let{health:c}of e)for(let u of c.issues)r[u]=(r[u]||0)+1;let d=Object.entries(r).sort(([,c],[,u])=>u-c).slice(0,10);d.length>0&&$t(d,t.length),a.info("");}function pt(e,t,n){a.heading("\u{1F4CA} Summary"),a.info(""),a.info(` Total documents: ${l.bold(String(e.length))}`),a.info(` Average score: ${l.bold(we(t))}%`),a.info(""),a.info(` ${A.excellent} Excellent (80+): ${n.excellent}`),a.info(` ${A.good} Good (60-79): ${n.good}`),a.info(` ${A.fair} Fair (40-59): ${n.fair}`),a.info(` ${A.poor} Poor (1-39): ${n.poor}`),a.info(` ${A.missing} Missing (0): ${n.missing}`);}var mt={excellent:"Excellent",good:"Good",fair:"Fair",poor:"Poor",missing:"Missing"},ht={excellent:l.green,good:l.blue,fair:l.yellow,poor:l.red,missing:l.dim};function yt(e,t=11){var s,i;let n=((s=mt[e])!=null?s:e).padEnd(t);return ((i=ht[e])!=null?i:(r=>r))(n)}function St(e){a.heading("\u{1F4CB} Documents"),a.info("");let t=Math.min(44,Math.max(5,...e.map(({doc:r})=>(r.title||r._id).length))),n=11,o=14,s=5,i=l.dim("\u2500".repeat(s+2+n+2+o+2+t));a.info(` ${l.dim("Score".padEnd(s))} ${l.dim("Status".padEnd(n))} ${l.dim("Type".padEnd(o))} ${l.dim("Title")}`),a.info(` ${i}`);for(let{doc:r,health:d}of e){let c=$e(r.title||r._id,t),u=String(d.score).padStart(s),f=yt(d.status,n),g=l.dim($e(r._type,o).padEnd(o));a.info(` ${we(d.score,u)} ${f} ${g} ${c}`);}}function $t(e,t){a.heading("\u{1F50D} Top Issues"),a.info("");for(let[n,o]of e){let s=Math.round(o/t*100);a.info(` ${l.red(String(o).padStart(4))} docs (${s}%) ${n}`);}}function Se(e){a.info(""),a.info(` ${l.dim("\u2500".repeat(52))}`),a.info(` ${l.dim("Connection info")}`),a.info(` ${l.dim("Project ID:")} ${l.white(e.projectId)} ${l.dim(`\u2190 ${e.sources.projectId}`)}`),a.info(` ${l.dim("Dataset: ")} ${l.white(e.dataset)} ${l.dim(`\u2190 ${e.sources.dataset}`)}`),a.info(` ${l.dim("Token: ")} ${l.dim(e.sources.token)}`),a.info("");}function we(e,t){let n=t!=null?t:String(e);return e>=80?l.green(n):e>=60?l.blue(n):e>=40?l.yellow(n):e>0?l.red(n):l.dim(n)}function $e(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e}H();var Z="1.6.5",ve=[l.cyan,e=>l.bold(l.blue(e)),l.blue,l.magenta,l.red,l.yellow],w=ve[Math.floor(Math.random()*ve.length)],Ce=["Run `seofields doctor` first to verify your setup is correct.","Use `seofields report --format summary` for a quick overview.","Export your SEO data before making bulk changes: `seofields export -o backup.json`.","Add `seoFields` type to every document schema to track SEO health.","A meta title between 50\u201360 characters gets the best click-through rate.","Meta descriptions should be 140\u2013160 characters for optimal display.","Open Graph images should be 1200\xD7630px for best social sharing.","Use `seofields config --baseUrl=https://yoursite.com` to enable canonical URLs.","Twitter Card images require a separate `twitterImage` field for best results.","Schema.org markup helps search engines understand your content structure.","Use `seofields report --types post,page` to filter by document type.","The `healthDashboard` tool gives a live view of SEO scores inside Sanity Studio.","Keywords should appear naturally \u2014 avoid keyword stuffing.","Every page needs a unique meta title. Duplicates hurt rankings.","Use `seofields export --format csv` to open SEO data in a spreadsheet.","Set `noIndex: true` on internal or draft pages to prevent indexing.","OG title and meta title can differ \u2014 OG is optimised for social, meta for search.","Run `seofields report` after content migrations to catch regressions.","Use `seofields init --schema-org` to add structured data support.","Structured data (Schema.org) can unlock rich results in Google Search.","A score of 80+ is excellent. Aim to get all key pages there.","The `seoPreview` option shows a live Google-style preview inside the editor.","Use `seofields report` in CI to catch SEO regressions before they ship.","Use `--dataset staging` to run reports against your staging dataset.","Keep your license key private \u2014 never commit it to version control.","Use `seofields config --healthDashboard.showDocumentId=true` for easier debugging.","Use `seofields export --format json --output report.json` to save SEO data to a file.","Alt text on images improves both accessibility and image search ranking.","Canonical URLs prevent duplicate content penalties from search engines.","A missing OG image means social shares show a blank card \u2014 always add one.","Use descriptive slugs: `/blog/seo-tips` ranks better than `/blog/post-123`.","Re-run `seofields report` monthly to track your SEO health over time.","The `types` filter helps you focus reports on high-priority content.","Use `seofields export` to back up SEO data before schema migrations.","Short meta titles (under 30 chars) waste valuable search result space.","Long meta descriptions get truncated \u2014 stay under 160 characters.","Twitter Cards need `twitter:card`, `twitter:title`, and `twitter:image` at minimum.","Use `seofields doctor` after upgrading the plugin to catch breaking changes.","JSON export includes health scores \u2014 pipe it into your analytics pipeline.","Set a `defaultHiddenFields` list to keep the SEO panel focused.","Internal links pass authority \u2014 make sure important pages are well-linked.","Page speed is a ranking factor \u2014 optimise images referenced in SEO fields.","Use consistent brand phrasing in meta titles across your site.","Structured data errors can be tested at schema.org/validator.","Focus first on pages with the most traffic, not just the lowest scores.","Add SEO review to your content publishing checklist.","Use `seofields config --healthDashboard.licenseKey=KEY` to unlock premium features.","The CSV export makes it easy to share SEO audits with non-technical stakeholders.","Every image on your page should have descriptive alt text.","Avoid duplicate meta descriptions \u2014 each page needs its own."];function bt(){return Ce[Math.floor(Math.random()*Ce.length)]}var wt=62,V=wt-2;function vt(e,t){let n=e.split(" "),o=[],s="";for(let i of n)s.length+(s?1:0)+i.length>t?(s&&o.push(s),s=i):s=s?`${s} ${i}`:i;return s&&o.push(s),o}function F(e,t){let n=V-3,o=" ".repeat(Math.max(0,n-t));return ` ${w("\u2502")} ${e}${o} ${w("\u2502")}`}function M(){return F("",0)}function xe(){let e="\u2500".repeat(V),t=bt(),n=V-3-5,o=vt(t,n);console.log(),console.log(` ${w(`\u256D${e}\u256E`)}`),console.log(M()),console.log(F(`${l.bold(l.green("seofields"))}${l.dim(` v${Z}`)}`,`seofields v${Z}`.length)),console.log(F(l.dim("SEO tooling for Sanity CMS \u2014 manage from your terminal"),54)),console.log(M());for(let i=0;i<o.length;i++){let r=i===0?`${l.dim("Tip: ")}${l.white(o[i])}`:` ${l.white(o[i])}`,d=5+o[i].length;console.log(F(r,d));}console.log(M()),console.log(` ${w(`\u2570${e}\u256F`)}`),console.log(),console.log(` ${l.bold("Commands")}`),console.log();let s=[["init ","Add seofields() to sanity.config"],["config ","Update plugin configuration options"],["create-config ","Create seofields.cli.ts / .js interactively"],["report ","SEO health report across all documents"],["export ","Export SEO fields as JSON or CSV"],["doctor ","Check setup, deps & config"]];for(let[i,r]of s)console.log(` ${w(i)} ${l.dim(r)}`);console.log(),console.log(` ${l.dim("Run ")}${l.white("seofields <command> --help")}${l.dim(" for details")}`),console.log();}function P(e,t){let n="\u2500".repeat(V),o=`seofields \u203A ${e}`;return ["",` ${w(`\u256D${n}\u256E`)}`,M(),F(`${l.bold(l.green("seofields"))}${w(" \u203A ")}${l.bold(w(e))}`,o.length),F(l.dim(t),t.length),M(),` ${w(`\u2570${n}\u256F`)}`].join(`
65
65
  `)}program.name("seofields").description("CLI for sanity-plugin-seofields \u2014 manage SEO from your terminal").version(Z).action(xe);program.command("config",{isDefault:false}).description("Update seofields() configuration in your sanity.config file").addHelpText("before",P("config","Update seofields() configuration in your sanity.config file")).allowUnknownOption().allowExcessArguments().helpOption("-h, --help","Show help and all available options").addHelpText("after",["",` ${l.bold("Usage")}`,` ${l.dim("Pass any option as")} ${l.white("--key=value")}${l.dim(". Nested keys use dot notation.")}`,"",` ${l.bold("Top-level options")}`,` ${w("--baseUrl")}${l.dim("=<url>").padEnd(38)} ${l.dim("Canonical base URL")}`,` ${w("--seoPreview")}${l.dim("=<bool>").padEnd(36)} ${l.dim("Enable live Google-style preview")}`,` ${w("--defaultHiddenFields")}${l.dim("=<f1,f2>").padEnd(28)} ${l.dim("Comma-separated fields to hide")}`,` ${w("--types")}${l.dim("=<t1,t2>").padEnd(40)} ${l.dim("Document types that have SEO fields")}`,"",` ${l.bold("healthDashboard options")} ${l.dim("(prefix: --healthDashboard.<key>)")}`,` ${w("--healthDashboard.licenseKey")}${l.dim("=<key>").padEnd(22)} ${l.dim("License key for premium features")}`,` ${w("--healthDashboard.toolTitle")}${l.dim("=<str>").padEnd(23)} ${l.dim("Custom title shown in Studio")}`,` ${w("--healthDashboard.showDocumentId")}${l.dim("=<bool>").padEnd(17)} ${l.dim("Show document _id in dashboard")}`,` ${w("--healthDashboard.showHealthScore")}${l.dim("=<bool>").padEnd(16)} ${l.dim("Show score badge per document")}`,` ${w("--healthDashboard.query.types")}${l.dim("=<t1,t2>").padEnd(21)} ${l.dim("Filter dashboard by document types")}`,"",` ${l.bold("Examples")}`,` ${l.white("seofields config --baseUrl=https://mysite.com")}`,` ${l.white("seofields config --healthDashboard.licenseKey=SEOF-1234")}`,` ${l.white("seofields config --healthDashboard.showDocumentId=true --seoPreview=true")}`,` ${l.white("seofields config --healthDashboard.query.types=post,page")}`,` ${l.white("seofields config --defaultHiddenFields=metaImage,openGraphUrl")}`,""].join(`
66
66
  `)).action(()=>{let e=process.argv.indexOf("config"),t=e>=0?process.argv.slice(e+1):[];de(t);});program.command("create-config").description("Interactively create a seofields.cli.ts / .js config file").addHelpText("before",P("create-config","Interactively create a seofields.cli.ts / .js config file")).action(ue);program.command("init").description("Add seofields() plugin to your sanity.config file").addHelpText("before",P("init","Add seofields() plugin to your sanity.config file")).option("--preview","Enable SEO preview in the plugin config").option("--dashboard","Explicitly enable the SEO Health Dashboard").option("--no-dashboard","Disable the SEO Health Dashboard").option("--schema-org","Also add schemaOrg() plugin registration").addHelpText("after",["",` ${l.dim("Note: ")}${l.yellow("--preview")}${l.dim(", ")}${l.yellow("--dashboard")}${l.dim(", and ")}${l.yellow("--no-dashboard")}${l.dim(" only apply during initial setup.")}`,` ${l.dim(" If seofields() is already in your config, use:")}`,"",` ${l.dim(" ")}${l.white("seofields config --healthDashboard=true --seoPreview=true")}`,""].join(`
67
67
  `)).action(ye);program.command("export").description("Export all documents with SEO fields as JSON or CSV").addHelpText("before",P("export","Export all documents with SEO fields as JSON or CSV")).option("-p, --project-id <id>","Sanity project ID").option("-d, --dataset <name>","Sanity dataset name").option("-t, --token <token>","Sanity API token").option("--types <types>","Comma-separated document types to export (e.g. post,page)").option("-f, --format <format>","Output format: json or csv","json").option("-o, --output <path>","Output file path (defaults to stdout)").action(me);program.command("report").description("Generate an SEO health report for all documents").addHelpText("before",P("report","Generate an SEO health report for all documents")).option("-p, --project-id <id>","Sanity project ID").option("-d, --dataset <name>","Sanity dataset name").option("-t, --token <token>","Sanity API token").option("--types <types>","Comma-separated document types to include").option("--format <format>",'Output format: table or summary (default: "table")',"table").action(be);program.command("doctor").description("Check plugin configuration, dependencies, and setup").addHelpText("before",P("doctor","Check plugin configuration, dependencies, and setup")).option("--cwd <path>","Working directory to check",process.cwd()).action(ge);var _=process.argv.slice(2);_.length===1&&(_[0]==="--help"||_[0]==="-h")&&(xe(),process.exit(0));if(_.length>=2&&_[_.length-1]==="help"){let e=program.commands.find(t=>t.name()===_[0]);e&&(e.help(),process.exit(0));}program.parse();