@power-seo/react
Advanced tools
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/context.ts","../src/head-tags.ts","../src/components/DefaultSEO.ts","../src/components/SEO.ts","../src/components/OpenGraph.ts","../src/components/TwitterCard.ts","../src/components/Canonical.ts","../src/components/Robots.ts","../src/components/Hreflang.ts","../src/components/Breadcrumb.ts"],"names":["createContext","useContext","createElement","Fragment","resolveTitle","buildMetaTags","buildLinkTags","buildOpenGraphTags","buildTwitterTags","resolveCanonical","buildRobotsContent"],"mappings":";;;;;;AAOO,IAAM,UAAA,GAAaA,oBAAgC,IAAI;AAKvD,SAAS,aAAA,GAAkC;AAChD,EAAA,OAAOC,iBAAW,UAAU,CAAA;AAC9B;ACFO,SAAS,eAAe,IAAA,EAAiB;AAC9C,EAAA,OAAOC,mBAAA;AAAA,IACLC,cAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAG,IAAA,CAAK,GAAA,CAAI,CAAC,KAAK,CAAA,KAAM;AACtB,MAAA,MAAM,KAAA,GAAgC,EAAE,OAAA,EAAS,GAAA,CAAI,OAAA,EAAQ;AAC7D,MAAA,IAAI,GAAA,CAAI,IAAA,EAAM,KAAA,CAAM,IAAA,GAAO,GAAA,CAAI,IAAA;AAC/B,MAAA,IAAI,GAAA,CAAI,QAAA,EAAU,KAAA,CAAM,QAAA,GAAW,GAAA,CAAI,QAAA;AACvC,MAAA,IAAI,GAAA,CAAI,SAAA,EAAW,KAAA,CAAM,SAAA,GAAY,GAAA,CAAI,SAAA;AACzC,MAAA,KAAA,CAAM,GAAA,GAAM,QAAQ,GAAA,CAAI,IAAA,IAAQ,IAAI,QAAA,IAAY,GAAA,CAAI,aAAa,CAAC,CAAA,CAAA;AAClE,MAAA,OAAOD,mBAAA,CAAc,QAAQ,KAAK,CAAA;AAAA,IACpC,CAAC;AAAA,GACH;AACF;AAKO,SAAS,eAAe,IAAA,EAAiB;AAC9C,EAAA,OAAOA,mBAAA;AAAA,IACLC,cAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAG,IAAA,CAAK,GAAA,CAAI,CAAC,KAAK,CAAA,KAAM;AACtB,MAAA,MAAM,KAAA,GAA4C;AAAA,QAChD,KAAK,GAAA,CAAI,GAAA;AAAA,QACT,MAAM,GAAA,CAAI,IAAA;AAAA,QACV,KAAK,CAAA,KAAA,EAAQ,GAAA,CAAI,GAAG,CAAA,CAAA,EAAI,GAAA,CAAI,YAAY,CAAC,CAAA;AAAA,OAC3C;AACA,MAAA,IAAI,GAAA,CAAI,QAAA,EAAU,KAAA,CAAM,QAAA,GAAW,GAAA,CAAI,QAAA;AACvC,MAAA,IAAI,GAAA,CAAI,IAAA,EAAM,KAAA,CAAM,IAAA,GAAO,GAAA,CAAI,IAAA;AAC/B,MAAA,IAAI,GAAA,CAAI,KAAA,EAAO,KAAA,CAAM,KAAA,GAAQ,GAAA,CAAI,KAAA;AACjC,MAAA,IAAI,GAAA,CAAI,KAAA,EAAO,KAAA,CAAM,KAAA,GAAQ,GAAA,CAAI,KAAA;AACjC,MAAA,IAAI,GAAA,CAAI,EAAA,EAAI,KAAA,CAAM,EAAA,GAAK,GAAA,CAAI,EAAA;AAC3B,MAAA,IAAI,GAAA,CAAI,WAAA,EAAa,KAAA,CAAM,WAAA,GAAc,GAAA,CAAI,WAAA;AAC7C,MAAA,OAAOD,mBAAA,CAAc,QAAQ,KAAK,CAAA;AAAA,IACpC,CAAC;AAAA,GACH;AACF;;;ACfO,SAAS,UAAA,CAAW,EAAE,QAAA,EAAU,GAAG,QAAO,EAAoB;AACnE,EAAA,MAAM,KAAA,GAAQE,kBAAa,MAAM,CAAA;AACjC,EAAA,MAAM,QAAA,GAAWC,mBAAc,MAAM,CAAA;AACrC,EAAA,MAAM,QAAA,GAAWC,mBAAc,MAAM,CAAA;AAErC,EAAA,OAAOJ,mBAAAA;AAAA,IACL,UAAA,CAAW,QAAA;AAAA,IACX,EAAE,OAAO,MAAA,EAAO;AAAA,IAChB,KAAA,GAAQA,mBAAAA,CAAc,OAAA,EAAS,IAAA,EAAM,KAAK,CAAA,GAAI,IAAA;AAAA,IAC9C,eAAe,QAAQ,CAAA;AAAA,IACvB,eAAe,QAAQ,CAAA;AAAA,IACvB;AAAA,GACF;AACF;ACXO,SAAS,IAAI,KAAA,EAAiB;AACnC,EAAA,MAAM,WAAW,aAAA,EAAc;AAG/B,EAAA,MAAM,MAAA,GAAoB;AAAA,IACxB,GAAG,QAAA;AAAA,IACH,GAAG,KAAA;AAAA;AAAA,IAEH,SAAA,EAAW;AAAA,MACT,GAAG,QAAA,EAAU,SAAA;AAAA,MACb,GAAG,KAAA,CAAM,SAAA;AAAA,MACT,MAAA,EAAQ,KAAA,CAAM,SAAA,EAAW,MAAA,IAAU,UAAU,SAAA,EAAW,MAAA;AAAA,MACxD,MAAA,EAAQ,KAAA,CAAM,SAAA,EAAW,MAAA,IAAU,UAAU,SAAA,EAAW;AAAA,KAC1D;AAAA,IACA,OAAA,EAAS;AAAA,MACP,GAAG,QAAA,EAAU,OAAA;AAAA,MACb,GAAG,KAAA,CAAM;AAAA,KACX;AAAA,IACA,kBAAA,EAAoB;AAAA,MAClB,GAAI,QAAA,EAAU,kBAAA,IAAsB,EAAC;AAAA,MACrC,GAAI,KAAA,CAAM,kBAAA,IAAsB;AAAC,KACnC;AAAA,IACA,kBAAA,EAAoB;AAAA,MAClB,GAAI,QAAA,EAAU,kBAAA,IAAsB,EAAC;AAAA,MACrC,GAAI,KAAA,CAAM,kBAAA,IAAsB;AAAC,KACnC;AAAA,IACA,kBAAA,EAAoB,KAAA,CAAM,kBAAA,IAAsB,QAAA,EAAU,kBAAA;AAAA;AAAA,IAE1D,aAAA,EAAe,KAAA,CAAM,aAAA,IAAiB,QAAA,EAAU;AAAA,GAClD;AAEA,EAAA,MAAM,KAAA,GAAQE,kBAAa,MAAM,CAAA;AACjC,EAAA,MAAM,QAAA,GAAWC,mBAAc,MAAM,CAAA;AACrC,EAAA,MAAM,QAAA,GAAWC,mBAAc,MAAM,CAAA;AAErC,EAAA,OAAOJ,mBAAAA;AAAA,IACLC,cAAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA,GAAQD,mBAAAA,CAAc,OAAA,EAAS,IAAA,EAAM,KAAK,CAAA,GAAI,IAAA;AAAA,IAC9C,eAAe,QAAQ,CAAA;AAAA,IACvB,eAAe,QAAQ;AAAA,GACzB;AACF;AC3CO,SAAS,UAAU,KAAA,EAAuB;AAC/C,EAAA,MAAM,IAAA,GAAOK,wBAAmB,KAAK,CAAA;AACrC,EAAA,OAAOL,mBAAAA,CAAcC,cAAAA,EAAU,IAAA,EAAM,cAAA,CAAe,IAAI,CAAC,CAAA;AAC3D;ACXO,SAAS,YAAY,KAAA,EAAyB;AACnD,EAAA,MAAM,IAAA,GAAOK,sBAAiB,KAAK,CAAA;AACnC,EAAA,OAAON,mBAAAA,CAAcC,cAAAA,EAAU,IAAA,EAAM,cAAA,CAAe,IAAI,CAAC,CAAA;AAC3D;ACJO,SAAS,UAAU,EAAE,GAAA,EAAK,OAAA,EAAS,aAAA,GAAgB,OAAM,EAAmB;AACjF,EAAA,IAAI,SAAA,GAAY,OAAA,GAAUM,qBAAA,CAAiB,OAAA,EAAS,GAAG,CAAA,GAAI,GAAA;AAE3D,EAAA,IAAI,aAAA,IAAiB,CAAC,SAAA,CAAU,QAAA,CAAS,GAAG,CAAA,EAAG;AAC7C,IAAA,SAAA,IAAa,GAAA;AAAA,EACf,CAAA,MAAA,IAAW,CAAC,aAAA,IAAiB,SAAA,CAAU,QAAA,CAAS,GAAG,CAAA,IAAK,SAAA,KAAc,GAAA,CAAI,OAAA,CAAQ,KAAA,EAAO,EAAE,IAAI,GAAA,EAAK;AAIpG,EAAA,OAAOP,oBAAc,MAAA,EAAQ;AAAA,IAC3B,GAAA,EAAK,WAAA;AAAA,IACL,IAAA,EAAM;AAAA,GACP,CAAA;AACH;ACpBO,SAAS,OAAO,KAAA,EAAoB;AACzC,EAAA,MAAM,OAAA,GAAUQ,wBAAmB,KAAK,CAAA;AACxC,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACrB,EAAA,OAAOR,oBAAc,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,SAAS,CAAA;AAC1D;ACMO,SAAS,QAAA,CAAS,EAAE,UAAA,EAAY,QAAA,EAAS,EAAkB;AAChE,EAAA,MAAM,QAAQ,UAAA,CAAW,GAAA;AAAA,IAAI,CAAC,GAAA,KAC5BA,mBAAAA,CAAc,MAAA,EAAQ;AAAA,MACpB,GAAA,EAAK,CAAA,SAAA,EAAY,GAAA,CAAI,QAAQ,CAAA,CAAA;AAAA,MAC7B,GAAA,EAAK,WAAA;AAAA,MACL,UAAU,GAAA,CAAI,QAAA;AAAA,MACd,MAAM,GAAA,CAAI;AAAA,KACX;AAAA,GACH;AAEA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,KAAA,CAAM,IAAA;AAAA,MACJA,oBAAc,MAAA,EAAQ;AAAA,QACpB,GAAA,EAAK,oBAAA;AAAA,QACL,GAAA,EAAK,WAAA;AAAA,QACL,QAAA,EAAU,WAAA;AAAA,QACV,IAAA,EAAM;AAAA,OACP;AAAA,KACH;AAAA,EACF;AAEA,EAAA,OAAOA,mBAAAA,CAAcC,cAAAA,EAAU,IAAA,EAAM,GAAG,KAAK,CAAA;AAC/C;ACXO,SAAS,UAAA,CAAW;AAAA,EACzB,KAAA;AAAA,EACA,SAAA,GAAY,KAAA;AAAA,EACZ,SAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,aAAA,GAAgB;AAClB,CAAA,EAAoB;AAElB,EAAA,MAAM,UAAA,GAAa;AAAA,IACjB,UAAA,EAAY,oBAAA;AAAA,IACZ,OAAA,EAAS,gBAAA;AAAA,IACT,eAAA,EAAiB,KAAA,CAAM,GAAA,CAAI,CAAC,MAAM,KAAA,MAAW;AAAA,MAC3C,OAAA,EAAS,UAAA;AAAA,MACT,UAAU,KAAA,GAAQ,CAAA;AAAA,MAClB,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,GAAI,KAAK,GAAA,GAAM,EAAE,MAAM,IAAA,CAAK,GAAA,KAAQ;AAAC,KACvC,CAAE;AAAA,GACJ;AAEA,EAAA,MAAM,WAA+C,EAAC;AAGtD,EAAA,MAAM,kBAAsD,EAAC;AAC7D,EAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,EAAM,KAAA,KAAU;AAC7B,IAAA,IAAI,QAAQ,CAAA,EAAG;AACb,MAAA,eAAA,CAAgB,IAAA;AAAA,QACdD,mBAAAA,CAAc,MAAA,EAAQ,EAAE,GAAA,EAAK,CAAA,IAAA,EAAO,KAAK,CAAA,CAAA,EAAI,aAAA,EAAe,MAAA,EAAO,EAAG,SAAS;AAAA,OACjF;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAAS,KAAA,KAAU,KAAA,CAAM,MAAA,GAAS,CAAA;AAExC,IAAA,IAAI,IAAA,CAAK,GAAA,IAAO,CAAC,MAAA,EAAQ;AACvB,MAAA,eAAA,CAAgB,IAAA;AAAA,QACdA,mBAAAA;AAAA,UACE,GAAA;AAAA,UACA,EAAE,KAAK,CAAA,KAAA,EAAQ,KAAK,IAAI,IAAA,EAAM,IAAA,CAAK,GAAA,EAAK,SAAA,EAAW,aAAA,EAAc;AAAA,UACjE,IAAA,CAAK;AAAA;AACP,OACF;AAAA,IACF,CAAA,MAAO;AACL,MAAA,eAAA,CAAgB,IAAA;AAAA,QACdA,mBAAAA;AAAA,UACE,MAAA;AAAA,UACA;AAAA,YACE,GAAA,EAAK,QAAQ,KAAK,CAAA,CAAA;AAAA,YAClB,SAAA,EAAW,SAAS,eAAA,GAAkB,MAAA;AAAA,YACtC,cAAA,EAAgB,SAAS,MAAA,GAAS;AAAA,WACpC;AAAA,UACA,IAAA,CAAK;AAAA;AACP,OACF;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AAED,EAAA,QAAA,CAAS,IAAA;AAAA,IACPA,mBAAAA;AAAA,MACE,KAAA;AAAA,MACA,EAAE,YAAA,EAAc,YAAA,EAAc,SAAA,EAAU;AAAA,MACxCA,oBAAc,IAAA,EAAM,EAAE,OAAO,EAAE,SAAA,EAAW,QAAQ,OAAA,EAAS,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,SAAS,MAAA,EAAQ,QAAA,EAAU,QAAgB,EAAE,EAAG,GAAG,eAAe;AAAA;AAC7I,GACF;AAGA,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,QAAA,CAAS,IAAA;AAAA,MACPA,oBAAc,QAAA,EAAU;AAAA,QACtB,GAAA,EAAK,mBAAA;AAAA,QACL,IAAA,EAAM,qBAAA;AAAA,QACN,yBAAyB,EAAE,MAAA,EAAQ,IAAA,CAAK,SAAA,CAAU,UAAU,CAAA;AAAE,OAC/D;AAAA,KACH;AAAA,EACF;AAEA,EAAA,OAAOA,mBAAAA,CAAcC,cAAAA,EAAU,IAAA,EAAM,GAAG,QAAQ,CAAA;AAClD","file":"index.cjs","sourcesContent":["// ============================================================================\r\n// @power-seo/react — SEO Context for Default Configuration\r\n// ============================================================================\r\n\r\nimport { createContext, useContext } from 'react';\r\nimport type { SEOConfig } from '@power-seo/core';\r\n\r\nexport const SEOContext = createContext<SEOConfig | null>(null);\r\n\r\n/**\r\n * Hook to access the default SEO configuration from the nearest DefaultSEO provider.\r\n */\r\nexport function useDefaultSEO(): SEOConfig | null {\r\n return useContext(SEOContext);\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — Head Tag Rendering (React 19 native + fallback)\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { MetaTag, LinkTag } from '@power-seo/core';\r\n\r\n/**\r\n * Render meta tags as React elements.\r\n * In React 19, these automatically hoist to <head>.\r\n * In React 18, wrap with a Helmet provider or use a framework's Head component.\r\n */\r\nexport function renderMetaTags(tags: MetaTag[]) {\r\n return createElement(\r\n Fragment,\r\n null,\r\n ...tags.map((tag, i) => {\r\n const props: Record<string, string> = { content: tag.content };\r\n if (tag.name) props.name = tag.name;\r\n if (tag.property) props.property = tag.property;\r\n if (tag.httpEquiv) props.httpEquiv = tag.httpEquiv;\r\n props.key = `meta-${tag.name ?? tag.property ?? tag.httpEquiv ?? i}`;\r\n return createElement('meta', props);\r\n }),\r\n );\r\n}\r\n\r\n/**\r\n * Render link tags as React elements.\r\n */\r\nexport function renderLinkTags(tags: LinkTag[]) {\r\n return createElement(\r\n Fragment,\r\n null,\r\n ...tags.map((tag, i) => {\r\n const props: Record<string, string | undefined> = {\r\n rel: tag.rel,\r\n href: tag.href,\r\n key: `link-${tag.rel}-${tag.hreflang ?? i}`,\r\n };\r\n if (tag.hreflang) props.hrefLang = tag.hreflang;\r\n if (tag.type) props.type = tag.type;\r\n if (tag.sizes) props.sizes = tag.sizes;\r\n if (tag.media) props.media = tag.media;\r\n if (tag.as) props.as = tag.as;\r\n if (tag.crossOrigin) props.crossOrigin = tag.crossOrigin;\r\n return createElement('link', props);\r\n }),\r\n );\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <DefaultSEO> Component\r\n// ============================================================================\r\n\r\nimport { createElement } from 'react';\r\nimport type { ReactNode } from 'react';\r\nimport type { SEOConfig } from '@power-seo/core';\r\nimport { buildMetaTags, buildLinkTags, resolveTitle } from '@power-seo/core';\r\nimport { SEOContext } from '../context.js';\r\nimport { renderMetaTags, renderLinkTags } from '../head-tags.js';\r\n\r\nexport interface DefaultSEOProps extends SEOConfig {\r\n children?: ReactNode;\r\n}\r\n\r\n/**\r\n * Provide global default SEO configuration.\r\n * Renders default meta tags and wraps children with SEO context.\r\n *\r\n * @example\r\n * ```tsx\r\n * <DefaultSEO\r\n * titleTemplate=\"%s | My Site\"\r\n * defaultTitle=\"My Site\"\r\n * description=\"Default description\"\r\n * openGraph={{\r\n * type: 'website',\r\n * siteName: 'My Site',\r\n * }}\r\n * >\r\n * <App />\r\n * </DefaultSEO>\r\n * ```\r\n */\r\nexport function DefaultSEO({ children, ...config }: DefaultSEOProps) {\r\n const title = resolveTitle(config);\r\n const metaTags = buildMetaTags(config);\r\n const linkTags = buildLinkTags(config);\r\n\r\n return createElement(\r\n SEOContext.Provider,\r\n { value: config },\r\n title ? createElement('title', null, title) : null,\r\n renderMetaTags(metaTags),\r\n renderLinkTags(linkTags),\r\n children,\r\n );\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <SEO> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { SEOConfig } from '@power-seo/core';\r\nimport { buildMetaTags, buildLinkTags, resolveTitle } from '@power-seo/core';\r\nimport { useDefaultSEO } from '../context.js';\r\nimport { renderMetaTags, renderLinkTags } from '../head-tags.js';\r\n\r\nexport type SEOProps = SEOConfig;\r\n\r\n/**\r\n * All-in-one SEO component for per-page meta tag management.\r\n * Merges with DefaultSEO context if available.\r\n *\r\n * In React 19, <title>, <meta>, and <link> tags automatically hoist to <head>.\r\n * In React 18, use with a Helmet provider or framework Head component.\r\n *\r\n * @example\r\n * ```tsx\r\n * <SEO\r\n * title=\"About Us\"\r\n * description=\"Learn about our company\"\r\n * canonical=\"https://example.com/about\"\r\n * openGraph={{\r\n * title: 'About Us',\r\n * description: 'Learn about our company',\r\n * images: [{ url: 'https://example.com/about-og.jpg', width: 1200, height: 630 }],\r\n * }}\r\n * twitter={{\r\n * cardType: 'summary_large_image',\r\n * }}\r\n * />\r\n * ```\r\n */\r\nexport function SEO(props: SEOProps) {\r\n const defaults = useDefaultSEO();\r\n\r\n // Merge page config with defaults\r\n const config: SEOConfig = {\r\n ...defaults,\r\n ...props,\r\n // Deep merge for nested objects\r\n openGraph: {\r\n ...defaults?.openGraph,\r\n ...props.openGraph,\r\n images: props.openGraph?.images ?? defaults?.openGraph?.images,\r\n videos: props.openGraph?.videos ?? defaults?.openGraph?.videos,\r\n },\r\n twitter: {\r\n ...defaults?.twitter,\r\n ...props.twitter,\r\n },\r\n additionalMetaTags: [\r\n ...(defaults?.additionalMetaTags ?? []),\r\n ...(props.additionalMetaTags ?? []),\r\n ],\r\n additionalLinkTags: [\r\n ...(defaults?.additionalLinkTags ?? []),\r\n ...(props.additionalLinkTags ?? []),\r\n ],\r\n languageAlternates: props.languageAlternates ?? defaults?.languageAlternates,\r\n // Use page-specific title template or default\r\n titleTemplate: props.titleTemplate ?? defaults?.titleTemplate,\r\n };\r\n\r\n const title = resolveTitle(config);\r\n const metaTags = buildMetaTags(config);\r\n const linkTags = buildLinkTags(config);\r\n\r\n return createElement(\r\n Fragment,\r\n null,\r\n title ? createElement('title', null, title) : null,\r\n renderMetaTags(metaTags),\r\n renderLinkTags(linkTags),\r\n );\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <OpenGraph> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { OpenGraphConfig } from '@power-seo/core';\r\nimport { buildOpenGraphTags } from '@power-seo/core';\r\nimport { renderMetaTags } from '../head-tags.js';\r\n\r\nexport type OpenGraphProps = OpenGraphConfig;\r\n\r\n/**\r\n * Render Open Graph meta tags.\r\n *\r\n * @example\r\n * ```tsx\r\n * <OpenGraph\r\n * type=\"article\"\r\n * title=\"My Article\"\r\n * description=\"Article description\"\r\n * url=\"https://example.com/article\"\r\n * images={[{\r\n * url: 'https://example.com/og.jpg',\r\n * width: 1200,\r\n * height: 630,\r\n * alt: 'Article image',\r\n * }]}\r\n * article={{\r\n * publishedTime: '2025-01-01',\r\n * authors: ['https://example.com/author'],\r\n * tags: ['react', 'seo'],\r\n * }}\r\n * />\r\n * ```\r\n */\r\nexport function OpenGraph(props: OpenGraphProps) {\r\n const tags = buildOpenGraphTags(props);\r\n return createElement(Fragment, null, renderMetaTags(tags));\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <TwitterCard> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { TwitterCardConfig } from '@power-seo/core';\r\nimport { buildTwitterTags } from '@power-seo/core';\r\nimport { renderMetaTags } from '../head-tags.js';\r\n\r\nexport type TwitterCardProps = TwitterCardConfig;\r\n\r\n/**\r\n * Render Twitter Card meta tags.\r\n *\r\n * @example\r\n * ```tsx\r\n * <TwitterCard\r\n * cardType=\"summary_large_image\"\r\n * site=\"@mysite\"\r\n * creator=\"@author\"\r\n * title=\"My Article\"\r\n * description=\"Article description\"\r\n * image=\"https://example.com/twitter.jpg\"\r\n * imageAlt=\"Twitter card image\"\r\n * />\r\n * ```\r\n */\r\nexport function TwitterCard(props: TwitterCardProps) {\r\n const tags = buildTwitterTags(props);\r\n return createElement(Fragment, null, renderMetaTags(tags));\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <Canonical> Component\r\n// ============================================================================\r\n\r\nimport { createElement } from 'react';\r\nimport { resolveCanonical } from '@power-seo/core';\r\n\r\nexport interface CanonicalProps {\r\n /** The canonical URL (absolute or relative to baseUrl) */\r\n url: string;\r\n /** Base URL for resolving relative paths */\r\n baseUrl?: string;\r\n /** Whether to add trailing slash (default: false) */\r\n trailingSlash?: boolean;\r\n}\r\n\r\n/**\r\n * Render a canonical link tag.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Canonical url=\"https://example.com/blog/post\" />\r\n * // or with base URL:\r\n * <Canonical url=\"/blog/post\" baseUrl=\"https://example.com\" />\r\n * ```\r\n */\r\nexport function Canonical({ url, baseUrl, trailingSlash = false }: CanonicalProps) {\r\n let canonical = baseUrl ? resolveCanonical(baseUrl, url) : url;\r\n\r\n if (trailingSlash && !canonical.endsWith('/')) {\r\n canonical += '/';\r\n } else if (!trailingSlash && canonical.endsWith('/') && canonical !== url.replace(/\\/$/, '') + '/') {\r\n // Only strip trailing slash if it was explicitly added\r\n }\r\n\r\n return createElement('link', {\r\n rel: 'canonical',\r\n href: canonical,\r\n });\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <Robots> Component\r\n// ============================================================================\r\n\r\nimport { createElement } from 'react';\r\nimport type { RobotsDirective } from '@power-seo/core';\r\nimport { buildRobotsContent } from '@power-seo/core';\r\n\r\nexport type RobotsProps = RobotsDirective;\r\n\r\n/**\r\n * Render a robots meta tag with per-page directives.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Robots index={false} follow={true} maxSnippet={150} />\r\n * // Renders: <meta name=\"robots\" content=\"noindex, follow, max-snippet:150\" />\r\n * ```\r\n */\r\nexport function Robots(props: RobotsProps) {\r\n const content = buildRobotsContent(props);\r\n if (!content) return null;\r\n return createElement('meta', { name: 'robots', content });\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <Hreflang> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { HreflangConfig } from '@power-seo/core';\r\n\r\nexport interface HreflangProps {\r\n /** Language alternates */\r\n alternates: HreflangConfig[];\r\n /** Whether to include x-default (usually same as default language) */\r\n xDefault?: string;\r\n}\r\n\r\n/**\r\n * Render hreflang link tags for multi-language pages.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Hreflang\r\n * alternates={[\r\n * { hrefLang: 'en', href: 'https://example.com/en/page' },\r\n * { hrefLang: 'fr', href: 'https://example.com/fr/page' },\r\n * { hrefLang: 'de', href: 'https://example.com/de/page' },\r\n * ]}\r\n * xDefault=\"https://example.com/en/page\"\r\n * />\r\n * ```\r\n */\r\nexport function Hreflang({ alternates, xDefault }: HreflangProps) {\r\n const links = alternates.map((alt) =>\r\n createElement('link', {\r\n key: `hreflang-${alt.hrefLang}`,\r\n rel: 'alternate',\r\n hrefLang: alt.hrefLang,\r\n href: alt.href,\r\n }),\r\n );\r\n\r\n if (xDefault) {\r\n links.push(\r\n createElement('link', {\r\n key: 'hreflang-x-default',\r\n rel: 'alternate',\r\n hrefLang: 'x-default',\r\n href: xDefault,\r\n }),\r\n );\r\n }\r\n\r\n return createElement(Fragment, null, ...links);\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <Breadcrumb> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\n\r\nexport interface BreadcrumbItem {\r\n name: string;\r\n url?: string;\r\n}\r\n\r\nexport interface BreadcrumbProps {\r\n /** Breadcrumb items from root to current page */\r\n items: BreadcrumbItem[];\r\n /** Separator between items (default: \" / \") */\r\n separator?: string;\r\n /** CSS class for the nav element */\r\n className?: string;\r\n /** CSS class for each link */\r\n linkClassName?: string;\r\n /** CSS class for the current (last) item */\r\n activeClassName?: string;\r\n /** Whether to render the JSON-LD alongside the visual breadcrumb (default: true) */\r\n includeJsonLd?: boolean;\r\n}\r\n\r\n/**\r\n * Visual breadcrumb navigation with optional BreadcrumbList JSON-LD.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Breadcrumb\r\n * items={[\r\n * { name: 'Home', url: '/' },\r\n * { name: 'Blog', url: '/blog' },\r\n * { name: 'Current Post' },\r\n * ]}\r\n * />\r\n * ```\r\n */\r\nexport function Breadcrumb({\r\n items,\r\n separator = ' / ',\r\n className,\r\n linkClassName,\r\n activeClassName,\r\n includeJsonLd = true,\r\n}: BreadcrumbProps) {\r\n // Build JSON-LD schema\r\n const jsonLdData = {\r\n '@context': 'https://schema.org' as const,\r\n '@type': 'BreadcrumbList' as const,\r\n itemListElement: items.map((item, index) => ({\r\n '@type': 'ListItem' as const,\r\n position: index + 1,\r\n name: item.name,\r\n ...(item.url ? { item: item.url } : {}),\r\n })),\r\n };\r\n\r\n const children: ReturnType<typeof createElement>[] = [];\r\n\r\n // Render visual breadcrumb\r\n const breadcrumbItems: ReturnType<typeof createElement>[] = [];\r\n items.forEach((item, index) => {\r\n if (index > 0) {\r\n breadcrumbItems.push(\r\n createElement('span', { key: `sep-${index}`, 'aria-hidden': 'true' }, separator),\r\n );\r\n }\r\n\r\n const isLast = index === items.length - 1;\r\n\r\n if (item.url && !isLast) {\r\n breadcrumbItems.push(\r\n createElement(\r\n 'a',\r\n { key: `item-${index}`, href: item.url, className: linkClassName },\r\n item.name,\r\n ),\r\n );\r\n } else {\r\n breadcrumbItems.push(\r\n createElement(\r\n 'span',\r\n {\r\n key: `item-${index}`,\r\n className: isLast ? activeClassName : undefined,\r\n 'aria-current': isLast ? 'page' : undefined,\r\n },\r\n item.name,\r\n ),\r\n );\r\n }\r\n });\r\n\r\n children.push(\r\n createElement(\r\n 'nav',\r\n { 'aria-label': 'Breadcrumb', className },\r\n createElement('ol', { style: { listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexWrap: 'wrap' as const } }, ...breadcrumbItems),\r\n ),\r\n );\r\n\r\n // Render JSON-LD\r\n if (includeJsonLd) {\r\n children.push(\r\n createElement('script', {\r\n key: 'breadcrumb-jsonld',\r\n type: 'application/ld+json',\r\n dangerouslySetInnerHTML: { __html: JSON.stringify(jsonLdData) },\r\n }),\r\n );\r\n }\r\n\r\n return createElement(Fragment, null, ...children);\r\n}\r\n"]} | ||
| {"version":3,"sources":["../src/context.ts","../src/head-tags.ts","../src/components/DefaultSEO.ts","../src/components/SEO.ts","../src/components/OpenGraph.ts","../src/components/TwitterCard.ts","../src/components/Canonical.ts","../src/components/Robots.ts","../src/components/Hreflang.ts","../src/components/Breadcrumb.ts"],"names":["createContext","useContext","createElement","Fragment","resolveTitle","buildMetaTags","buildLinkTags","buildOpenGraphTags","buildTwitterTags","resolveCanonical","buildRobotsContent"],"mappings":"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","file":"index.cjs","sourcesContent":["// ============================================================================\n// @power-seo/react — SEO Context for Default Configuration\n// ============================================================================\n\nimport { createContext, useContext } from 'react';\nimport type { SEOConfig } from '@power-seo/core';\n\nexport const SEOContext = createContext<SEOConfig | null>(null);\n\n/**\n * Hook to access the default SEO configuration from the nearest DefaultSEO provider.\n */\nexport function useDefaultSEO(): SEOConfig | null {\n return useContext(SEOContext);\n}\n","// ============================================================================\n// @power-seo/react — Head Tag Rendering (React 19 native + fallback)\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { MetaTag, LinkTag } from '@power-seo/core';\n\n/**\n * Render meta tags as React elements.\n * In React 19, these automatically hoist to <head>.\n * In React 18, wrap with a Helmet provider or use a framework's Head component.\n */\nexport function renderMetaTags(tags: MetaTag[]) {\n return createElement(\n Fragment,\n null,\n ...tags.map((tag, i) => {\n const props: Record<string, string> = { content: tag.content };\n if (tag.name) props.name = tag.name;\n if (tag.property) props.property = tag.property;\n if (tag.httpEquiv) props.httpEquiv = tag.httpEquiv;\n props.key = `meta-${tag.name ?? tag.property ?? tag.httpEquiv ?? i}`;\n return createElement('meta', props);\n }),\n );\n}\n\n/**\n * Render link tags as React elements.\n */\nexport function renderLinkTags(tags: LinkTag[]) {\n return createElement(\n Fragment,\n null,\n ...tags.map((tag, i) => {\n const props: Record<string, string | undefined> = {\n rel: tag.rel,\n href: tag.href,\n key: `link-${tag.rel}-${tag.hreflang ?? i}`,\n };\n if (tag.hreflang) props.hrefLang = tag.hreflang;\n if (tag.type) props.type = tag.type;\n if (tag.sizes) props.sizes = tag.sizes;\n if (tag.media) props.media = tag.media;\n if (tag.as) props.as = tag.as;\n if (tag.crossOrigin) props.crossOrigin = tag.crossOrigin;\n return createElement('link', props);\n }),\n );\n}\n","// ============================================================================\n// @power-seo/react — <DefaultSEO> Component\n// ============================================================================\n\nimport { createElement } from 'react';\nimport type { ReactNode } from 'react';\nimport type { SEOConfig } from '@power-seo/core';\nimport { buildMetaTags, buildLinkTags, resolveTitle } from '@power-seo/core';\nimport { SEOContext } from '../context.js';\nimport { renderMetaTags, renderLinkTags } from '../head-tags.js';\n\nexport interface DefaultSEOProps extends SEOConfig {\n children?: ReactNode;\n}\n\n/**\n * Provide global default SEO configuration.\n * Renders default meta tags and wraps children with SEO context.\n *\n * @example\n * ```tsx\n * <DefaultSEO\n * titleTemplate=\"%s | My Site\"\n * defaultTitle=\"My Site\"\n * description=\"Default description\"\n * openGraph={{\n * type: 'website',\n * siteName: 'My Site',\n * }}\n * >\n * <App />\n * </DefaultSEO>\n * ```\n */\nexport function DefaultSEO({ children, ...config }: DefaultSEOProps) {\n const title = resolveTitle(config);\n const metaTags = buildMetaTags(config);\n const linkTags = buildLinkTags(config);\n\n return createElement(\n SEOContext.Provider,\n { value: config },\n title ? createElement('title', null, title) : null,\n renderMetaTags(metaTags),\n renderLinkTags(linkTags),\n children,\n );\n}\n","// ============================================================================\n// @power-seo/react — <SEO> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { SEOConfig } from '@power-seo/core';\nimport { buildMetaTags, buildLinkTags, resolveTitle } from '@power-seo/core';\nimport { useDefaultSEO } from '../context.js';\nimport { renderMetaTags, renderLinkTags } from '../head-tags.js';\n\nexport type SEOProps = SEOConfig;\n\n/**\n * All-in-one SEO component for per-page meta tag management.\n * Merges with DefaultSEO context if available.\n *\n * In React 19, <title>, <meta>, and <link> tags automatically hoist to <head>.\n * In React 18, use with a Helmet provider or framework Head component.\n *\n * @example\n * ```tsx\n * <SEO\n * title=\"About Us\"\n * description=\"Learn about our company\"\n * canonical=\"https://example.com/about\"\n * openGraph={{\n * title: 'About Us',\n * description: 'Learn about our company',\n * images: [{ url: 'https://example.com/about-og.jpg', width: 1200, height: 630 }],\n * }}\n * twitter={{\n * cardType: 'summary_large_image',\n * }}\n * />\n * ```\n */\nexport function SEO(props: SEOProps) {\n const defaults = useDefaultSEO();\n\n // Merge page config with defaults\n const config: SEOConfig = {\n ...defaults,\n ...props,\n // Deep merge for nested objects\n openGraph: {\n ...defaults?.openGraph,\n ...props.openGraph,\n images: props.openGraph?.images ?? defaults?.openGraph?.images,\n videos: props.openGraph?.videos ?? defaults?.openGraph?.videos,\n },\n twitter: {\n ...defaults?.twitter,\n ...props.twitter,\n },\n additionalMetaTags: [\n ...(defaults?.additionalMetaTags ?? []),\n ...(props.additionalMetaTags ?? []),\n ],\n additionalLinkTags: [\n ...(defaults?.additionalLinkTags ?? []),\n ...(props.additionalLinkTags ?? []),\n ],\n languageAlternates: props.languageAlternates ?? defaults?.languageAlternates,\n // Use page-specific title template or default\n titleTemplate: props.titleTemplate ?? defaults?.titleTemplate,\n };\n\n const title = resolveTitle(config);\n const metaTags = buildMetaTags(config);\n const linkTags = buildLinkTags(config);\n\n return createElement(\n Fragment,\n null,\n title ? createElement('title', null, title) : null,\n renderMetaTags(metaTags),\n renderLinkTags(linkTags),\n );\n}\n","// ============================================================================\n// @power-seo/react — <OpenGraph> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { OpenGraphConfig } from '@power-seo/core';\nimport { buildOpenGraphTags } from '@power-seo/core';\nimport { renderMetaTags } from '../head-tags.js';\n\nexport type OpenGraphProps = OpenGraphConfig;\n\n/**\n * Render Open Graph meta tags.\n *\n * @example\n * ```tsx\n * <OpenGraph\n * type=\"article\"\n * title=\"My Article\"\n * description=\"Article description\"\n * url=\"https://example.com/article\"\n * images={[{\n * url: 'https://example.com/og.jpg',\n * width: 1200,\n * height: 630,\n * alt: 'Article image',\n * }]}\n * article={{\n * publishedTime: '2025-01-01',\n * authors: ['https://example.com/author'],\n * tags: ['react', 'seo'],\n * }}\n * />\n * ```\n */\nexport function OpenGraph(props: OpenGraphProps) {\n const tags = buildOpenGraphTags(props);\n return createElement(Fragment, null, renderMetaTags(tags));\n}\n","// ============================================================================\n// @power-seo/react — <TwitterCard> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { TwitterCardConfig } from '@power-seo/core';\nimport { buildTwitterTags } from '@power-seo/core';\nimport { renderMetaTags } from '../head-tags.js';\n\nexport type TwitterCardProps = TwitterCardConfig;\n\n/**\n * Render Twitter Card meta tags.\n *\n * @example\n * ```tsx\n * <TwitterCard\n * cardType=\"summary_large_image\"\n * site=\"@mysite\"\n * creator=\"@author\"\n * title=\"My Article\"\n * description=\"Article description\"\n * image=\"https://example.com/twitter.jpg\"\n * imageAlt=\"Twitter card image\"\n * />\n * ```\n */\nexport function TwitterCard(props: TwitterCardProps) {\n const tags = buildTwitterTags(props);\n return createElement(Fragment, null, renderMetaTags(tags));\n}\n","// ============================================================================\n// @power-seo/react — <Canonical> Component\n// ============================================================================\n\nimport { createElement } from 'react';\nimport { resolveCanonical } from '@power-seo/core';\n\nexport interface CanonicalProps {\n /** The canonical URL (absolute or relative to baseUrl) */\n url: string;\n /** Base URL for resolving relative paths */\n baseUrl?: string;\n /** Whether to add trailing slash (default: false) */\n trailingSlash?: boolean;\n}\n\n/**\n * Render a canonical link tag.\n *\n * @example\n * ```tsx\n * <Canonical url=\"https://example.com/blog/post\" />\n * // or with base URL:\n * <Canonical url=\"/blog/post\" baseUrl=\"https://example.com\" />\n * ```\n */\nexport function Canonical({ url, baseUrl, trailingSlash = false }: CanonicalProps) {\n let canonical = baseUrl ? resolveCanonical(baseUrl, url) : url;\n\n if (trailingSlash && !canonical.endsWith('/')) {\n canonical += '/';\n } else if (!trailingSlash && canonical.endsWith('/') && canonical !== url.replace(/\\/$/, '') + '/') {\n // Only strip trailing slash if it was explicitly added\n }\n\n return createElement('link', {\n rel: 'canonical',\n href: canonical,\n });\n}\n","// ============================================================================\n// @power-seo/react — <Robots> Component\n// ============================================================================\n\nimport { createElement } from 'react';\nimport type { RobotsDirective } from '@power-seo/core';\nimport { buildRobotsContent } from '@power-seo/core';\n\nexport type RobotsProps = RobotsDirective;\n\n/**\n * Render a robots meta tag with per-page directives.\n *\n * @example\n * ```tsx\n * <Robots index={false} follow={true} maxSnippet={150} />\n * // Renders: <meta name=\"robots\" content=\"noindex, follow, max-snippet:150\" />\n * ```\n */\nexport function Robots(props: RobotsProps) {\n const content = buildRobotsContent(props);\n if (!content) return null;\n return createElement('meta', { name: 'robots', content });\n}\n","// ============================================================================\n// @power-seo/react — <Hreflang> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { HreflangConfig } from '@power-seo/core';\n\nexport interface HreflangProps {\n /** Language alternates */\n alternates: HreflangConfig[];\n /** Whether to include x-default (usually same as default language) */\n xDefault?: string;\n}\n\n/**\n * Render hreflang link tags for multi-language pages.\n *\n * @example\n * ```tsx\n * <Hreflang\n * alternates={[\n * { hrefLang: 'en', href: 'https://example.com/en/page' },\n * { hrefLang: 'fr', href: 'https://example.com/fr/page' },\n * { hrefLang: 'de', href: 'https://example.com/de/page' },\n * ]}\n * xDefault=\"https://example.com/en/page\"\n * />\n * ```\n */\nexport function Hreflang({ alternates, xDefault }: HreflangProps) {\n const links = alternates.map((alt) =>\n createElement('link', {\n key: `hreflang-${alt.hrefLang}`,\n rel: 'alternate',\n hrefLang: alt.hrefLang,\n href: alt.href,\n }),\n );\n\n if (xDefault) {\n links.push(\n createElement('link', {\n key: 'hreflang-x-default',\n rel: 'alternate',\n hrefLang: 'x-default',\n href: xDefault,\n }),\n );\n }\n\n return createElement(Fragment, null, ...links);\n}\n","// ============================================================================\n// @power-seo/react — <Breadcrumb> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\n\nexport interface BreadcrumbItem {\n name: string;\n url?: string;\n}\n\nexport interface BreadcrumbProps {\n /** Breadcrumb items from root to current page */\n items: BreadcrumbItem[];\n /** Separator between items (default: \" / \") */\n separator?: string;\n /** CSS class for the nav element */\n className?: string;\n /** CSS class for each link */\n linkClassName?: string;\n /** CSS class for the current (last) item */\n activeClassName?: string;\n /** Whether to render the JSON-LD alongside the visual breadcrumb (default: true) */\n includeJsonLd?: boolean;\n}\n\n/**\n * Visual breadcrumb navigation with optional BreadcrumbList JSON-LD.\n *\n * @example\n * ```tsx\n * <Breadcrumb\n * items={[\n * { name: 'Home', url: '/' },\n * { name: 'Blog', url: '/blog' },\n * { name: 'Current Post' },\n * ]}\n * />\n * ```\n */\nexport function Breadcrumb({\n items,\n separator = ' / ',\n className,\n linkClassName,\n activeClassName,\n includeJsonLd = true,\n}: BreadcrumbProps) {\n // Build JSON-LD schema\n const jsonLdData = {\n '@context': 'https://schema.org' as const,\n '@type': 'BreadcrumbList' as const,\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem' as const,\n position: index + 1,\n name: item.name,\n ...(item.url ? { item: item.url } : {}),\n })),\n };\n\n const children: ReturnType<typeof createElement>[] = [];\n\n // Render visual breadcrumb\n const breadcrumbItems: ReturnType<typeof createElement>[] = [];\n items.forEach((item, index) => {\n if (index > 0) {\n breadcrumbItems.push(\n createElement('span', { key: `sep-${index}`, 'aria-hidden': 'true' }, separator),\n );\n }\n\n const isLast = index === items.length - 1;\n\n if (item.url && !isLast) {\n breadcrumbItems.push(\n createElement(\n 'a',\n { key: `item-${index}`, href: item.url, className: linkClassName },\n item.name,\n ),\n );\n } else {\n breadcrumbItems.push(\n createElement(\n 'span',\n {\n key: `item-${index}`,\n className: isLast ? activeClassName : undefined,\n 'aria-current': isLast ? 'page' : undefined,\n },\n item.name,\n ),\n );\n }\n });\n\n children.push(\n createElement(\n 'nav',\n { 'aria-label': 'Breadcrumb', className },\n createElement('ol', { style: { listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexWrap: 'wrap' as const } }, ...breadcrumbItems),\n ),\n );\n\n // Render JSON-LD\n if (includeJsonLd) {\n children.push(\n createElement('script', {\n key: 'breadcrumb-jsonld',\n type: 'application/ld+json',\n dangerouslySetInnerHTML: { __html: JSON.stringify(jsonLdData) },\n }),\n );\n }\n\n return createElement(Fragment, null, ...children);\n}\n"]} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/context.ts","../src/head-tags.ts","../src/components/DefaultSEO.ts","../src/components/SEO.ts","../src/components/OpenGraph.ts","../src/components/TwitterCard.ts","../src/components/Canonical.ts","../src/components/Robots.ts","../src/components/Hreflang.ts","../src/components/Breadcrumb.ts"],"names":["createElement","resolveTitle","buildMetaTags","buildLinkTags","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["// ============================================================================\r\n// @power-seo/react — SEO Context for Default Configuration\r\n// ============================================================================\r\n\r\nimport { createContext, useContext } from 'react';\r\nimport type { SEOConfig } from '@power-seo/core';\r\n\r\nexport const SEOContext = createContext<SEOConfig | null>(null);\r\n\r\n/**\r\n * Hook to access the default SEO configuration from the nearest DefaultSEO provider.\r\n */\r\nexport function useDefaultSEO(): SEOConfig | null {\r\n return useContext(SEOContext);\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — Head Tag Rendering (React 19 native + fallback)\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { MetaTag, LinkTag } from '@power-seo/core';\r\n\r\n/**\r\n * Render meta tags as React elements.\r\n * In React 19, these automatically hoist to <head>.\r\n * In React 18, wrap with a Helmet provider or use a framework's Head component.\r\n */\r\nexport function renderMetaTags(tags: MetaTag[]) {\r\n return createElement(\r\n Fragment,\r\n null,\r\n ...tags.map((tag, i) => {\r\n const props: Record<string, string> = { content: tag.content };\r\n if (tag.name) props.name = tag.name;\r\n if (tag.property) props.property = tag.property;\r\n if (tag.httpEquiv) props.httpEquiv = tag.httpEquiv;\r\n props.key = `meta-${tag.name ?? tag.property ?? tag.httpEquiv ?? i}`;\r\n return createElement('meta', props);\r\n }),\r\n );\r\n}\r\n\r\n/**\r\n * Render link tags as React elements.\r\n */\r\nexport function renderLinkTags(tags: LinkTag[]) {\r\n return createElement(\r\n Fragment,\r\n null,\r\n ...tags.map((tag, i) => {\r\n const props: Record<string, string | undefined> = {\r\n rel: tag.rel,\r\n href: tag.href,\r\n key: `link-${tag.rel}-${tag.hreflang ?? i}`,\r\n };\r\n if (tag.hreflang) props.hrefLang = tag.hreflang;\r\n if (tag.type) props.type = tag.type;\r\n if (tag.sizes) props.sizes = tag.sizes;\r\n if (tag.media) props.media = tag.media;\r\n if (tag.as) props.as = tag.as;\r\n if (tag.crossOrigin) props.crossOrigin = tag.crossOrigin;\r\n return createElement('link', props);\r\n }),\r\n );\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <DefaultSEO> Component\r\n// ============================================================================\r\n\r\nimport { createElement } from 'react';\r\nimport type { ReactNode } from 'react';\r\nimport type { SEOConfig } from '@power-seo/core';\r\nimport { buildMetaTags, buildLinkTags, resolveTitle } from '@power-seo/core';\r\nimport { SEOContext } from '../context.js';\r\nimport { renderMetaTags, renderLinkTags } from '../head-tags.js';\r\n\r\nexport interface DefaultSEOProps extends SEOConfig {\r\n children?: ReactNode;\r\n}\r\n\r\n/**\r\n * Provide global default SEO configuration.\r\n * Renders default meta tags and wraps children with SEO context.\r\n *\r\n * @example\r\n * ```tsx\r\n * <DefaultSEO\r\n * titleTemplate=\"%s | My Site\"\r\n * defaultTitle=\"My Site\"\r\n * description=\"Default description\"\r\n * openGraph={{\r\n * type: 'website',\r\n * siteName: 'My Site',\r\n * }}\r\n * >\r\n * <App />\r\n * </DefaultSEO>\r\n * ```\r\n */\r\nexport function DefaultSEO({ children, ...config }: DefaultSEOProps) {\r\n const title = resolveTitle(config);\r\n const metaTags = buildMetaTags(config);\r\n const linkTags = buildLinkTags(config);\r\n\r\n return createElement(\r\n SEOContext.Provider,\r\n { value: config },\r\n title ? createElement('title', null, title) : null,\r\n renderMetaTags(metaTags),\r\n renderLinkTags(linkTags),\r\n children,\r\n );\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <SEO> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { SEOConfig } from '@power-seo/core';\r\nimport { buildMetaTags, buildLinkTags, resolveTitle } from '@power-seo/core';\r\nimport { useDefaultSEO } from '../context.js';\r\nimport { renderMetaTags, renderLinkTags } from '../head-tags.js';\r\n\r\nexport type SEOProps = SEOConfig;\r\n\r\n/**\r\n * All-in-one SEO component for per-page meta tag management.\r\n * Merges with DefaultSEO context if available.\r\n *\r\n * In React 19, <title>, <meta>, and <link> tags automatically hoist to <head>.\r\n * In React 18, use with a Helmet provider or framework Head component.\r\n *\r\n * @example\r\n * ```tsx\r\n * <SEO\r\n * title=\"About Us\"\r\n * description=\"Learn about our company\"\r\n * canonical=\"https://example.com/about\"\r\n * openGraph={{\r\n * title: 'About Us',\r\n * description: 'Learn about our company',\r\n * images: [{ url: 'https://example.com/about-og.jpg', width: 1200, height: 630 }],\r\n * }}\r\n * twitter={{\r\n * cardType: 'summary_large_image',\r\n * }}\r\n * />\r\n * ```\r\n */\r\nexport function SEO(props: SEOProps) {\r\n const defaults = useDefaultSEO();\r\n\r\n // Merge page config with defaults\r\n const config: SEOConfig = {\r\n ...defaults,\r\n ...props,\r\n // Deep merge for nested objects\r\n openGraph: {\r\n ...defaults?.openGraph,\r\n ...props.openGraph,\r\n images: props.openGraph?.images ?? defaults?.openGraph?.images,\r\n videos: props.openGraph?.videos ?? defaults?.openGraph?.videos,\r\n },\r\n twitter: {\r\n ...defaults?.twitter,\r\n ...props.twitter,\r\n },\r\n additionalMetaTags: [\r\n ...(defaults?.additionalMetaTags ?? []),\r\n ...(props.additionalMetaTags ?? []),\r\n ],\r\n additionalLinkTags: [\r\n ...(defaults?.additionalLinkTags ?? []),\r\n ...(props.additionalLinkTags ?? []),\r\n ],\r\n languageAlternates: props.languageAlternates ?? defaults?.languageAlternates,\r\n // Use page-specific title template or default\r\n titleTemplate: props.titleTemplate ?? defaults?.titleTemplate,\r\n };\r\n\r\n const title = resolveTitle(config);\r\n const metaTags = buildMetaTags(config);\r\n const linkTags = buildLinkTags(config);\r\n\r\n return createElement(\r\n Fragment,\r\n null,\r\n title ? createElement('title', null, title) : null,\r\n renderMetaTags(metaTags),\r\n renderLinkTags(linkTags),\r\n );\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <OpenGraph> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { OpenGraphConfig } from '@power-seo/core';\r\nimport { buildOpenGraphTags } from '@power-seo/core';\r\nimport { renderMetaTags } from '../head-tags.js';\r\n\r\nexport type OpenGraphProps = OpenGraphConfig;\r\n\r\n/**\r\n * Render Open Graph meta tags.\r\n *\r\n * @example\r\n * ```tsx\r\n * <OpenGraph\r\n * type=\"article\"\r\n * title=\"My Article\"\r\n * description=\"Article description\"\r\n * url=\"https://example.com/article\"\r\n * images={[{\r\n * url: 'https://example.com/og.jpg',\r\n * width: 1200,\r\n * height: 630,\r\n * alt: 'Article image',\r\n * }]}\r\n * article={{\r\n * publishedTime: '2025-01-01',\r\n * authors: ['https://example.com/author'],\r\n * tags: ['react', 'seo'],\r\n * }}\r\n * />\r\n * ```\r\n */\r\nexport function OpenGraph(props: OpenGraphProps) {\r\n const tags = buildOpenGraphTags(props);\r\n return createElement(Fragment, null, renderMetaTags(tags));\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <TwitterCard> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { TwitterCardConfig } from '@power-seo/core';\r\nimport { buildTwitterTags } from '@power-seo/core';\r\nimport { renderMetaTags } from '../head-tags.js';\r\n\r\nexport type TwitterCardProps = TwitterCardConfig;\r\n\r\n/**\r\n * Render Twitter Card meta tags.\r\n *\r\n * @example\r\n * ```tsx\r\n * <TwitterCard\r\n * cardType=\"summary_large_image\"\r\n * site=\"@mysite\"\r\n * creator=\"@author\"\r\n * title=\"My Article\"\r\n * description=\"Article description\"\r\n * image=\"https://example.com/twitter.jpg\"\r\n * imageAlt=\"Twitter card image\"\r\n * />\r\n * ```\r\n */\r\nexport function TwitterCard(props: TwitterCardProps) {\r\n const tags = buildTwitterTags(props);\r\n return createElement(Fragment, null, renderMetaTags(tags));\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <Canonical> Component\r\n// ============================================================================\r\n\r\nimport { createElement } from 'react';\r\nimport { resolveCanonical } from '@power-seo/core';\r\n\r\nexport interface CanonicalProps {\r\n /** The canonical URL (absolute or relative to baseUrl) */\r\n url: string;\r\n /** Base URL for resolving relative paths */\r\n baseUrl?: string;\r\n /** Whether to add trailing slash (default: false) */\r\n trailingSlash?: boolean;\r\n}\r\n\r\n/**\r\n * Render a canonical link tag.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Canonical url=\"https://example.com/blog/post\" />\r\n * // or with base URL:\r\n * <Canonical url=\"/blog/post\" baseUrl=\"https://example.com\" />\r\n * ```\r\n */\r\nexport function Canonical({ url, baseUrl, trailingSlash = false }: CanonicalProps) {\r\n let canonical = baseUrl ? resolveCanonical(baseUrl, url) : url;\r\n\r\n if (trailingSlash && !canonical.endsWith('/')) {\r\n canonical += '/';\r\n } else if (!trailingSlash && canonical.endsWith('/') && canonical !== url.replace(/\\/$/, '') + '/') {\r\n // Only strip trailing slash if it was explicitly added\r\n }\r\n\r\n return createElement('link', {\r\n rel: 'canonical',\r\n href: canonical,\r\n });\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <Robots> Component\r\n// ============================================================================\r\n\r\nimport { createElement } from 'react';\r\nimport type { RobotsDirective } from '@power-seo/core';\r\nimport { buildRobotsContent } from '@power-seo/core';\r\n\r\nexport type RobotsProps = RobotsDirective;\r\n\r\n/**\r\n * Render a robots meta tag with per-page directives.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Robots index={false} follow={true} maxSnippet={150} />\r\n * // Renders: <meta name=\"robots\" content=\"noindex, follow, max-snippet:150\" />\r\n * ```\r\n */\r\nexport function Robots(props: RobotsProps) {\r\n const content = buildRobotsContent(props);\r\n if (!content) return null;\r\n return createElement('meta', { name: 'robots', content });\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <Hreflang> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\nimport type { HreflangConfig } from '@power-seo/core';\r\n\r\nexport interface HreflangProps {\r\n /** Language alternates */\r\n alternates: HreflangConfig[];\r\n /** Whether to include x-default (usually same as default language) */\r\n xDefault?: string;\r\n}\r\n\r\n/**\r\n * Render hreflang link tags for multi-language pages.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Hreflang\r\n * alternates={[\r\n * { hrefLang: 'en', href: 'https://example.com/en/page' },\r\n * { hrefLang: 'fr', href: 'https://example.com/fr/page' },\r\n * { hrefLang: 'de', href: 'https://example.com/de/page' },\r\n * ]}\r\n * xDefault=\"https://example.com/en/page\"\r\n * />\r\n * ```\r\n */\r\nexport function Hreflang({ alternates, xDefault }: HreflangProps) {\r\n const links = alternates.map((alt) =>\r\n createElement('link', {\r\n key: `hreflang-${alt.hrefLang}`,\r\n rel: 'alternate',\r\n hrefLang: alt.hrefLang,\r\n href: alt.href,\r\n }),\r\n );\r\n\r\n if (xDefault) {\r\n links.push(\r\n createElement('link', {\r\n key: 'hreflang-x-default',\r\n rel: 'alternate',\r\n hrefLang: 'x-default',\r\n href: xDefault,\r\n }),\r\n );\r\n }\r\n\r\n return createElement(Fragment, null, ...links);\r\n}\r\n","// ============================================================================\r\n// @power-seo/react — <Breadcrumb> Component\r\n// ============================================================================\r\n\r\nimport { createElement, Fragment } from 'react';\r\n\r\nexport interface BreadcrumbItem {\r\n name: string;\r\n url?: string;\r\n}\r\n\r\nexport interface BreadcrumbProps {\r\n /** Breadcrumb items from root to current page */\r\n items: BreadcrumbItem[];\r\n /** Separator between items (default: \" / \") */\r\n separator?: string;\r\n /** CSS class for the nav element */\r\n className?: string;\r\n /** CSS class for each link */\r\n linkClassName?: string;\r\n /** CSS class for the current (last) item */\r\n activeClassName?: string;\r\n /** Whether to render the JSON-LD alongside the visual breadcrumb (default: true) */\r\n includeJsonLd?: boolean;\r\n}\r\n\r\n/**\r\n * Visual breadcrumb navigation with optional BreadcrumbList JSON-LD.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Breadcrumb\r\n * items={[\r\n * { name: 'Home', url: '/' },\r\n * { name: 'Blog', url: '/blog' },\r\n * { name: 'Current Post' },\r\n * ]}\r\n * />\r\n * ```\r\n */\r\nexport function Breadcrumb({\r\n items,\r\n separator = ' / ',\r\n className,\r\n linkClassName,\r\n activeClassName,\r\n includeJsonLd = true,\r\n}: BreadcrumbProps) {\r\n // Build JSON-LD schema\r\n const jsonLdData = {\r\n '@context': 'https://schema.org' as const,\r\n '@type': 'BreadcrumbList' as const,\r\n itemListElement: items.map((item, index) => ({\r\n '@type': 'ListItem' as const,\r\n position: index + 1,\r\n name: item.name,\r\n ...(item.url ? { item: item.url } : {}),\r\n })),\r\n };\r\n\r\n const children: ReturnType<typeof createElement>[] = [];\r\n\r\n // Render visual breadcrumb\r\n const breadcrumbItems: ReturnType<typeof createElement>[] = [];\r\n items.forEach((item, index) => {\r\n if (index > 0) {\r\n breadcrumbItems.push(\r\n createElement('span', { key: `sep-${index}`, 'aria-hidden': 'true' }, separator),\r\n );\r\n }\r\n\r\n const isLast = index === items.length - 1;\r\n\r\n if (item.url && !isLast) {\r\n breadcrumbItems.push(\r\n createElement(\r\n 'a',\r\n { key: `item-${index}`, href: item.url, className: linkClassName },\r\n item.name,\r\n ),\r\n );\r\n } else {\r\n breadcrumbItems.push(\r\n createElement(\r\n 'span',\r\n {\r\n key: `item-${index}`,\r\n className: isLast ? activeClassName : undefined,\r\n 'aria-current': isLast ? 'page' : undefined,\r\n },\r\n item.name,\r\n ),\r\n );\r\n }\r\n });\r\n\r\n children.push(\r\n createElement(\r\n 'nav',\r\n { 'aria-label': 'Breadcrumb', className },\r\n createElement('ol', { style: { listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexWrap: 'wrap' as const } }, ...breadcrumbItems),\r\n ),\r\n );\r\n\r\n // Render JSON-LD\r\n if (includeJsonLd) {\r\n children.push(\r\n createElement('script', {\r\n key: 'breadcrumb-jsonld',\r\n type: 'application/ld+json',\r\n dangerouslySetInnerHTML: { __html: JSON.stringify(jsonLdData) },\r\n }),\r\n );\r\n }\r\n\r\n return createElement(Fragment, null, ...children);\r\n}\r\n"]} | ||
| {"version":3,"sources":["../src/context.ts","../src/head-tags.ts","../src/components/DefaultSEO.ts","../src/components/SEO.ts","../src/components/OpenGraph.ts","../src/components/TwitterCard.ts","../src/components/Canonical.ts","../src/components/Robots.ts","../src/components/Hreflang.ts","../src/components/Breadcrumb.ts"],"names":["createElement","resolveTitle","buildMetaTags","buildLinkTags","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["// ============================================================================\n// @power-seo/react — SEO Context for Default Configuration\n// ============================================================================\n\nimport { createContext, useContext } from 'react';\nimport type { SEOConfig } from '@power-seo/core';\n\nexport const SEOContext = createContext<SEOConfig | null>(null);\n\n/**\n * Hook to access the default SEO configuration from the nearest DefaultSEO provider.\n */\nexport function useDefaultSEO(): SEOConfig | null {\n return useContext(SEOContext);\n}\n","// ============================================================================\n// @power-seo/react — Head Tag Rendering (React 19 native + fallback)\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { MetaTag, LinkTag } from '@power-seo/core';\n\n/**\n * Render meta tags as React elements.\n * In React 19, these automatically hoist to <head>.\n * In React 18, wrap with a Helmet provider or use a framework's Head component.\n */\nexport function renderMetaTags(tags: MetaTag[]) {\n return createElement(\n Fragment,\n null,\n ...tags.map((tag, i) => {\n const props: Record<string, string> = { content: tag.content };\n if (tag.name) props.name = tag.name;\n if (tag.property) props.property = tag.property;\n if (tag.httpEquiv) props.httpEquiv = tag.httpEquiv;\n props.key = `meta-${tag.name ?? tag.property ?? tag.httpEquiv ?? i}`;\n return createElement('meta', props);\n }),\n );\n}\n\n/**\n * Render link tags as React elements.\n */\nexport function renderLinkTags(tags: LinkTag[]) {\n return createElement(\n Fragment,\n null,\n ...tags.map((tag, i) => {\n const props: Record<string, string | undefined> = {\n rel: tag.rel,\n href: tag.href,\n key: `link-${tag.rel}-${tag.hreflang ?? i}`,\n };\n if (tag.hreflang) props.hrefLang = tag.hreflang;\n if (tag.type) props.type = tag.type;\n if (tag.sizes) props.sizes = tag.sizes;\n if (tag.media) props.media = tag.media;\n if (tag.as) props.as = tag.as;\n if (tag.crossOrigin) props.crossOrigin = tag.crossOrigin;\n return createElement('link', props);\n }),\n );\n}\n","// ============================================================================\n// @power-seo/react — <DefaultSEO> Component\n// ============================================================================\n\nimport { createElement } from 'react';\nimport type { ReactNode } from 'react';\nimport type { SEOConfig } from '@power-seo/core';\nimport { buildMetaTags, buildLinkTags, resolveTitle } from '@power-seo/core';\nimport { SEOContext } from '../context.js';\nimport { renderMetaTags, renderLinkTags } from '../head-tags.js';\n\nexport interface DefaultSEOProps extends SEOConfig {\n children?: ReactNode;\n}\n\n/**\n * Provide global default SEO configuration.\n * Renders default meta tags and wraps children with SEO context.\n *\n * @example\n * ```tsx\n * <DefaultSEO\n * titleTemplate=\"%s | My Site\"\n * defaultTitle=\"My Site\"\n * description=\"Default description\"\n * openGraph={{\n * type: 'website',\n * siteName: 'My Site',\n * }}\n * >\n * <App />\n * </DefaultSEO>\n * ```\n */\nexport function DefaultSEO({ children, ...config }: DefaultSEOProps) {\n const title = resolveTitle(config);\n const metaTags = buildMetaTags(config);\n const linkTags = buildLinkTags(config);\n\n return createElement(\n SEOContext.Provider,\n { value: config },\n title ? createElement('title', null, title) : null,\n renderMetaTags(metaTags),\n renderLinkTags(linkTags),\n children,\n );\n}\n","// ============================================================================\n// @power-seo/react — <SEO> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { SEOConfig } from '@power-seo/core';\nimport { buildMetaTags, buildLinkTags, resolveTitle } from '@power-seo/core';\nimport { useDefaultSEO } from '../context.js';\nimport { renderMetaTags, renderLinkTags } from '../head-tags.js';\n\nexport type SEOProps = SEOConfig;\n\n/**\n * All-in-one SEO component for per-page meta tag management.\n * Merges with DefaultSEO context if available.\n *\n * In React 19, <title>, <meta>, and <link> tags automatically hoist to <head>.\n * In React 18, use with a Helmet provider or framework Head component.\n *\n * @example\n * ```tsx\n * <SEO\n * title=\"About Us\"\n * description=\"Learn about our company\"\n * canonical=\"https://example.com/about\"\n * openGraph={{\n * title: 'About Us',\n * description: 'Learn about our company',\n * images: [{ url: 'https://example.com/about-og.jpg', width: 1200, height: 630 }],\n * }}\n * twitter={{\n * cardType: 'summary_large_image',\n * }}\n * />\n * ```\n */\nexport function SEO(props: SEOProps) {\n const defaults = useDefaultSEO();\n\n // Merge page config with defaults\n const config: SEOConfig = {\n ...defaults,\n ...props,\n // Deep merge for nested objects\n openGraph: {\n ...defaults?.openGraph,\n ...props.openGraph,\n images: props.openGraph?.images ?? defaults?.openGraph?.images,\n videos: props.openGraph?.videos ?? defaults?.openGraph?.videos,\n },\n twitter: {\n ...defaults?.twitter,\n ...props.twitter,\n },\n additionalMetaTags: [\n ...(defaults?.additionalMetaTags ?? []),\n ...(props.additionalMetaTags ?? []),\n ],\n additionalLinkTags: [\n ...(defaults?.additionalLinkTags ?? []),\n ...(props.additionalLinkTags ?? []),\n ],\n languageAlternates: props.languageAlternates ?? defaults?.languageAlternates,\n // Use page-specific title template or default\n titleTemplate: props.titleTemplate ?? defaults?.titleTemplate,\n };\n\n const title = resolveTitle(config);\n const metaTags = buildMetaTags(config);\n const linkTags = buildLinkTags(config);\n\n return createElement(\n Fragment,\n null,\n title ? createElement('title', null, title) : null,\n renderMetaTags(metaTags),\n renderLinkTags(linkTags),\n );\n}\n","// ============================================================================\n// @power-seo/react — <OpenGraph> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { OpenGraphConfig } from '@power-seo/core';\nimport { buildOpenGraphTags } from '@power-seo/core';\nimport { renderMetaTags } from '../head-tags.js';\n\nexport type OpenGraphProps = OpenGraphConfig;\n\n/**\n * Render Open Graph meta tags.\n *\n * @example\n * ```tsx\n * <OpenGraph\n * type=\"article\"\n * title=\"My Article\"\n * description=\"Article description\"\n * url=\"https://example.com/article\"\n * images={[{\n * url: 'https://example.com/og.jpg',\n * width: 1200,\n * height: 630,\n * alt: 'Article image',\n * }]}\n * article={{\n * publishedTime: '2025-01-01',\n * authors: ['https://example.com/author'],\n * tags: ['react', 'seo'],\n * }}\n * />\n * ```\n */\nexport function OpenGraph(props: OpenGraphProps) {\n const tags = buildOpenGraphTags(props);\n return createElement(Fragment, null, renderMetaTags(tags));\n}\n","// ============================================================================\n// @power-seo/react — <TwitterCard> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { TwitterCardConfig } from '@power-seo/core';\nimport { buildTwitterTags } from '@power-seo/core';\nimport { renderMetaTags } from '../head-tags.js';\n\nexport type TwitterCardProps = TwitterCardConfig;\n\n/**\n * Render Twitter Card meta tags.\n *\n * @example\n * ```tsx\n * <TwitterCard\n * cardType=\"summary_large_image\"\n * site=\"@mysite\"\n * creator=\"@author\"\n * title=\"My Article\"\n * description=\"Article description\"\n * image=\"https://example.com/twitter.jpg\"\n * imageAlt=\"Twitter card image\"\n * />\n * ```\n */\nexport function TwitterCard(props: TwitterCardProps) {\n const tags = buildTwitterTags(props);\n return createElement(Fragment, null, renderMetaTags(tags));\n}\n","// ============================================================================\n// @power-seo/react — <Canonical> Component\n// ============================================================================\n\nimport { createElement } from 'react';\nimport { resolveCanonical } from '@power-seo/core';\n\nexport interface CanonicalProps {\n /** The canonical URL (absolute or relative to baseUrl) */\n url: string;\n /** Base URL for resolving relative paths */\n baseUrl?: string;\n /** Whether to add trailing slash (default: false) */\n trailingSlash?: boolean;\n}\n\n/**\n * Render a canonical link tag.\n *\n * @example\n * ```tsx\n * <Canonical url=\"https://example.com/blog/post\" />\n * // or with base URL:\n * <Canonical url=\"/blog/post\" baseUrl=\"https://example.com\" />\n * ```\n */\nexport function Canonical({ url, baseUrl, trailingSlash = false }: CanonicalProps) {\n let canonical = baseUrl ? resolveCanonical(baseUrl, url) : url;\n\n if (trailingSlash && !canonical.endsWith('/')) {\n canonical += '/';\n } else if (!trailingSlash && canonical.endsWith('/') && canonical !== url.replace(/\\/$/, '') + '/') {\n // Only strip trailing slash if it was explicitly added\n }\n\n return createElement('link', {\n rel: 'canonical',\n href: canonical,\n });\n}\n","// ============================================================================\n// @power-seo/react — <Robots> Component\n// ============================================================================\n\nimport { createElement } from 'react';\nimport type { RobotsDirective } from '@power-seo/core';\nimport { buildRobotsContent } from '@power-seo/core';\n\nexport type RobotsProps = RobotsDirective;\n\n/**\n * Render a robots meta tag with per-page directives.\n *\n * @example\n * ```tsx\n * <Robots index={false} follow={true} maxSnippet={150} />\n * // Renders: <meta name=\"robots\" content=\"noindex, follow, max-snippet:150\" />\n * ```\n */\nexport function Robots(props: RobotsProps) {\n const content = buildRobotsContent(props);\n if (!content) return null;\n return createElement('meta', { name: 'robots', content });\n}\n","// ============================================================================\n// @power-seo/react — <Hreflang> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\nimport type { HreflangConfig } from '@power-seo/core';\n\nexport interface HreflangProps {\n /** Language alternates */\n alternates: HreflangConfig[];\n /** Whether to include x-default (usually same as default language) */\n xDefault?: string;\n}\n\n/**\n * Render hreflang link tags for multi-language pages.\n *\n * @example\n * ```tsx\n * <Hreflang\n * alternates={[\n * { hrefLang: 'en', href: 'https://example.com/en/page' },\n * { hrefLang: 'fr', href: 'https://example.com/fr/page' },\n * { hrefLang: 'de', href: 'https://example.com/de/page' },\n * ]}\n * xDefault=\"https://example.com/en/page\"\n * />\n * ```\n */\nexport function Hreflang({ alternates, xDefault }: HreflangProps) {\n const links = alternates.map((alt) =>\n createElement('link', {\n key: `hreflang-${alt.hrefLang}`,\n rel: 'alternate',\n hrefLang: alt.hrefLang,\n href: alt.href,\n }),\n );\n\n if (xDefault) {\n links.push(\n createElement('link', {\n key: 'hreflang-x-default',\n rel: 'alternate',\n hrefLang: 'x-default',\n href: xDefault,\n }),\n );\n }\n\n return createElement(Fragment, null, ...links);\n}\n","// ============================================================================\n// @power-seo/react — <Breadcrumb> Component\n// ============================================================================\n\nimport { createElement, Fragment } from 'react';\n\nexport interface BreadcrumbItem {\n name: string;\n url?: string;\n}\n\nexport interface BreadcrumbProps {\n /** Breadcrumb items from root to current page */\n items: BreadcrumbItem[];\n /** Separator between items (default: \" / \") */\n separator?: string;\n /** CSS class for the nav element */\n className?: string;\n /** CSS class for each link */\n linkClassName?: string;\n /** CSS class for the current (last) item */\n activeClassName?: string;\n /** Whether to render the JSON-LD alongside the visual breadcrumb (default: true) */\n includeJsonLd?: boolean;\n}\n\n/**\n * Visual breadcrumb navigation with optional BreadcrumbList JSON-LD.\n *\n * @example\n * ```tsx\n * <Breadcrumb\n * items={[\n * { name: 'Home', url: '/' },\n * { name: 'Blog', url: '/blog' },\n * { name: 'Current Post' },\n * ]}\n * />\n * ```\n */\nexport function Breadcrumb({\n items,\n separator = ' / ',\n className,\n linkClassName,\n activeClassName,\n includeJsonLd = true,\n}: BreadcrumbProps) {\n // Build JSON-LD schema\n const jsonLdData = {\n '@context': 'https://schema.org' as const,\n '@type': 'BreadcrumbList' as const,\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem' as const,\n position: index + 1,\n name: item.name,\n ...(item.url ? { item: item.url } : {}),\n })),\n };\n\n const children: ReturnType<typeof createElement>[] = [];\n\n // Render visual breadcrumb\n const breadcrumbItems: ReturnType<typeof createElement>[] = [];\n items.forEach((item, index) => {\n if (index > 0) {\n breadcrumbItems.push(\n createElement('span', { key: `sep-${index}`, 'aria-hidden': 'true' }, separator),\n );\n }\n\n const isLast = index === items.length - 1;\n\n if (item.url && !isLast) {\n breadcrumbItems.push(\n createElement(\n 'a',\n { key: `item-${index}`, href: item.url, className: linkClassName },\n item.name,\n ),\n );\n } else {\n breadcrumbItems.push(\n createElement(\n 'span',\n {\n key: `item-${index}`,\n className: isLast ? activeClassName : undefined,\n 'aria-current': isLast ? 'page' : undefined,\n },\n item.name,\n ),\n );\n }\n });\n\n children.push(\n createElement(\n 'nav',\n { 'aria-label': 'Breadcrumb', className },\n createElement('ol', { style: { listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexWrap: 'wrap' as const } }, ...breadcrumbItems),\n ),\n );\n\n // Render JSON-LD\n if (includeJsonLd) {\n children.push(\n createElement('script', {\n key: 'breadcrumb-jsonld',\n type: 'application/ld+json',\n dangerouslySetInnerHTML: { __html: JSON.stringify(jsonLdData) },\n }),\n );\n }\n\n return createElement(Fragment, null, ...children);\n}\n"]} |
+4
-4
| { | ||
| "name": "@power-seo/react", | ||
| "version": "1.0.0", | ||
| "version": "1.0.1", | ||
| "description": "Framework-agnostic React SEO components — meta tags, Open Graph, Twitter Card, breadcrumbs, and more", | ||
@@ -9,5 +9,5 @@ "license": "MIT", | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.js", | ||
| "require": "./dist/index.cjs", | ||
| "types": "./dist/index.d.ts" | ||
| "require": "./dist/index.cjs" | ||
| } | ||
@@ -62,3 +62,3 @@ }, | ||
| "type": "git", | ||
| "url": "git+https://github.com/cybercraftbd/power-seo.git", | ||
| "url": "git+https://github.com/CyberCraftBD/power-seo.git", | ||
| "directory": "packages/react" | ||
@@ -65,0 +65,0 @@ }, |
+97
-81
@@ -17,2 +17,3 @@ # @power-seo/react — React SEO Components for Meta Tags, Open Graph, Twitter Card, Robots & Breadcrumbs | ||
| **What it does** | ||
| - ✅ **All-in-one `<SEO>` component** — renders title, meta description, canonical, robots, Open Graph, and Twitter Card from one component | ||
@@ -25,2 +26,3 @@ - ✅ **Context-based defaults** — `<DefaultSEO>` at app root sets site-wide defaults; pages override selectively | ||
| **What it is not** | ||
| - ❌ **Not an App Router solution** — use `@power-seo/meta` for Next.js App Router `generateMetadata()` | ||
@@ -30,2 +32,3 @@ - ❌ **Not server-side only** — requires `react-helmet-async` under the hood; targets DOM-rendering React apps | ||
| **Recommended for** | ||
| - **Next.js Pages Router apps**, **Vite + React SPAs**, **Gatsby sites**, and **Create React App projects** | ||
@@ -38,2 +41,3 @@ | ||
| **The problem** | ||
| - **Scattered meta tag management** — teams add `<Helmet>` blocks inconsistently across hundreds of components | ||
@@ -44,2 +48,3 @@ - **Missing Open Graph images** — no single source of truth for default OG images leads to bare link previews | ||
| **Why developers care** | ||
| - **SEO:** Consistent title templates and canonical URLs prevent duplicate content penalties | ||
@@ -117,2 +122,3 @@ - **UX:** Correct OG and Twitter Card images dramatically increase social click-through | ||
| **What you should see** | ||
| - `<title>My Post Title | My Site</title>` in the DOM `<head>` | ||
@@ -148,2 +154,3 @@ - Correct `og:image`, `twitter:card`, and `link rel="canonical"` tags on every page | ||
| **Supported** | ||
| - ✅ Next.js Pages Router — works with `_app.tsx` and per-page `<SEO>` components | ||
@@ -155,2 +162,3 @@ - ✅ Vite + React — works in standard SPA setup | ||
| **Environment notes** | ||
| - **SSR/SSG:** Supported via `react-helmet-async` (SSR-safe) | ||
@@ -203,2 +211,3 @@ - **Edge runtime:** Not supported (requires DOM/React reconciler) | ||
| **Where it runs** | ||
| - **Client-side (CSR):** `react-helmet-async` manages `<head>` updates on navigation | ||
@@ -209,2 +218,3 @@ - **Server-side (SSR):** `HelmetProvider` collects head tags during render and serializes them into the initial HTML | ||
| **Data flow** | ||
| 1. **Input**: Page-level title, description, OG config, robots directive props | ||
@@ -219,10 +229,10 @@ 2. **Analysis**: `react-helmet-async` merges `<DefaultSEO>` defaults with `<SEO>` overrides | ||
| | Capability | next-seo | react-helmet | react-helmet-async | @power-seo/react | | ||
| |---|---:|---:|---:|---:| | ||
| | Typed robots directives | ✅ | ❌ | ❌ | ✅ | | ||
| | DefaultSEO context pattern | ✅ | ❌ | ❌ | ✅ | | ||
| | Hreflang support | ✅ | ❌ | ❌ | ✅ | | ||
| | Breadcrumb with JSON-LD | ✅ | ❌ | ❌ | ✅ | | ||
| | `max-snippet` / `max-image-preview` | ✅ | ❌ | ❌ | ✅ | | ||
| | TypeScript-first API | ✅ | ⚠️ | ⚠️ | ✅ | | ||
| | Capability | next-seo | react-helmet | react-helmet-async | @power-seo/react | | ||
| | ----------------------------------- | -------: | -----------: | -----------------: | ---------------: | | ||
| | Typed robots directives | ✅ | ❌ | ❌ | ✅ | | ||
| | DefaultSEO context pattern | ✅ | ❌ | ❌ | ✅ | | ||
| | Hreflang support | ✅ | ❌ | ❌ | ✅ | | ||
| | Breadcrumb with JSON-LD | ✅ | ❌ | ❌ | ✅ | | ||
| | `max-snippet` / `max-image-preview` | ✅ | ❌ | ❌ | ✅ | | ||
| | TypeScript-first API | ✅ | ⚠️ | ⚠️ | ✅ | | ||
@@ -235,30 +245,30 @@ --- | ||
| | Package | Install | Description | | ||
| |---------|---------|-------------| | ||
| | [`@power-seo/core`](https://www.npmjs.com/package/@power-seo/core) | `npm i @power-seo/core` | Framework-agnostic utilities, types, validators, and constants | | ||
| | [`@power-seo/react`](https://www.npmjs.com/package/@power-seo/react) | `npm i @power-seo/react` | React SEO components — meta, Open Graph, Twitter Card, robots, breadcrumbs | | ||
| | [`@power-seo/meta`](https://www.npmjs.com/package/@power-seo/meta) | `npm i @power-seo/meta` | SSR meta helpers for Next.js App Router, Remix v2, and generic SSR | | ||
| | [`@power-seo/schema`](https://www.npmjs.com/package/@power-seo/schema) | `npm i @power-seo/schema` | Type-safe JSON-LD structured data — 20 builders + 18 React components | | ||
| | [`@power-seo/content-analysis`](https://www.npmjs.com/package/@power-seo/content-analysis) | `npm i @power-seo/content-analysis` | Yoast-style SEO content scoring engine with React components | | ||
| | [`@power-seo/readability`](https://www.npmjs.com/package/@power-seo/readability) | `npm i @power-seo/readability` | Readability scoring — Flesch-Kincaid, Gunning Fog, Coleman-Liau, ARI | | ||
| | [`@power-seo/preview`](https://www.npmjs.com/package/@power-seo/preview) | `npm i @power-seo/preview` | SERP, Open Graph, and Twitter/X Card preview generators | | ||
| | [`@power-seo/sitemap`](https://www.npmjs.com/package/@power-seo/sitemap) | `npm i @power-seo/sitemap` | XML sitemap generation, streaming, index splitting, and validation | | ||
| | [`@power-seo/redirects`](https://www.npmjs.com/package/@power-seo/redirects) | `npm i @power-seo/redirects` | Redirect engine with Next.js, Remix, and Express adapters | | ||
| | [`@power-seo/links`](https://www.npmjs.com/package/@power-seo/links) | `npm i @power-seo/links` | Link graph analysis — orphan detection, suggestions, equity scoring | | ||
| | [`@power-seo/audit`](https://www.npmjs.com/package/@power-seo/audit) | `npm i @power-seo/audit` | Full SEO audit engine — meta, content, structure, performance rules | | ||
| | [`@power-seo/images`](https://www.npmjs.com/package/@power-seo/images) | `npm i @power-seo/images` | Image SEO — alt text, lazy loading, format analysis, image sitemaps | | ||
| | [`@power-seo/ai`](https://www.npmjs.com/package/@power-seo/ai) | `npm i @power-seo/ai` | LLM-agnostic AI prompt templates and parsers for SEO tasks | | ||
| | [`@power-seo/analytics`](https://www.npmjs.com/package/@power-seo/analytics) | `npm i @power-seo/analytics` | Merge GSC + audit data, trend analysis, ranking insights, dashboard | | ||
| | [`@power-seo/search-console`](https://www.npmjs.com/package/@power-seo/search-console) | `npm i @power-seo/search-console` | Google Search Console API — OAuth2, service account, URL inspection | | ||
| | [`@power-seo/integrations`](https://www.npmjs.com/package/@power-seo/integrations) | `npm i @power-seo/integrations` | Semrush and Ahrefs API clients with rate limiting and pagination | | ||
| | [`@power-seo/tracking`](https://www.npmjs.com/package/@power-seo/tracking) | `npm i @power-seo/tracking` | GA4, Clarity, PostHog, Plausible, Fathom — scripts + consent management | | ||
| | Package | Install | Description | | ||
| | ------------------------------------------------------------------------------------------ | ----------------------------------- | -------------------------------------------------------------------------- | | ||
| | [`@power-seo/core`](https://www.npmjs.com/package/@power-seo/core) | `npm i @power-seo/core` | Framework-agnostic utilities, types, validators, and constants | | ||
| | [`@power-seo/react`](https://www.npmjs.com/package/@power-seo/react) | `npm i @power-seo/react` | React SEO components — meta, Open Graph, Twitter Card, robots, breadcrumbs | | ||
| | [`@power-seo/meta`](https://www.npmjs.com/package/@power-seo/meta) | `npm i @power-seo/meta` | SSR meta helpers for Next.js App Router, Remix v2, and generic SSR | | ||
| | [`@power-seo/schema`](https://www.npmjs.com/package/@power-seo/schema) | `npm i @power-seo/schema` | Type-safe JSON-LD structured data — 20 builders + 18 React components | | ||
| | [`@power-seo/content-analysis`](https://www.npmjs.com/package/@power-seo/content-analysis) | `npm i @power-seo/content-analysis` | Yoast-style SEO content scoring engine with React components | | ||
| | [`@power-seo/readability`](https://www.npmjs.com/package/@power-seo/readability) | `npm i @power-seo/readability` | Readability scoring — Flesch-Kincaid, Gunning Fog, Coleman-Liau, ARI | | ||
| | [`@power-seo/preview`](https://www.npmjs.com/package/@power-seo/preview) | `npm i @power-seo/preview` | SERP, Open Graph, and Twitter/X Card preview generators | | ||
| | [`@power-seo/sitemap`](https://www.npmjs.com/package/@power-seo/sitemap) | `npm i @power-seo/sitemap` | XML sitemap generation, streaming, index splitting, and validation | | ||
| | [`@power-seo/redirects`](https://www.npmjs.com/package/@power-seo/redirects) | `npm i @power-seo/redirects` | Redirect engine with Next.js, Remix, and Express adapters | | ||
| | [`@power-seo/links`](https://www.npmjs.com/package/@power-seo/links) | `npm i @power-seo/links` | Link graph analysis — orphan detection, suggestions, equity scoring | | ||
| | [`@power-seo/audit`](https://www.npmjs.com/package/@power-seo/audit) | `npm i @power-seo/audit` | Full SEO audit engine — meta, content, structure, performance rules | | ||
| | [`@power-seo/images`](https://www.npmjs.com/package/@power-seo/images) | `npm i @power-seo/images` | Image SEO — alt text, lazy loading, format analysis, image sitemaps | | ||
| | [`@power-seo/ai`](https://www.npmjs.com/package/@power-seo/ai) | `npm i @power-seo/ai` | LLM-agnostic AI prompt templates and parsers for SEO tasks | | ||
| | [`@power-seo/analytics`](https://www.npmjs.com/package/@power-seo/analytics) | `npm i @power-seo/analytics` | Merge GSC + audit data, trend analysis, ranking insights, dashboard | | ||
| | [`@power-seo/search-console`](https://www.npmjs.com/package/@power-seo/search-console) | `npm i @power-seo/search-console` | Google Search Console API — OAuth2, service account, URL inspection | | ||
| | [`@power-seo/integrations`](https://www.npmjs.com/package/@power-seo/integrations) | `npm i @power-seo/integrations` | Semrush and Ahrefs API clients with rate limiting and pagination | | ||
| | [`@power-seo/tracking`](https://www.npmjs.com/package/@power-seo/tracking) | `npm i @power-seo/tracking` | GA4, Clarity, PostHog, Plausible, Fathom — scripts + consent management | | ||
| ### Ecosystem vs alternatives | ||
| | Need | Common approach | @power-seo approach | | ||
| |---|---|---| | ||
| | React meta tags | `next-seo` / `react-helmet` | `@power-seo/react` — typed components | | ||
| | App Router metadata | `generateMetadata()` | `@power-seo/meta` — typed helpers | | ||
| | JSON-LD structured data | Manual `<script>` tags | `@power-seo/schema` — typed builders | | ||
| | Sitemap generation | `next-sitemap` | `@power-seo/sitemap` — streaming + index | | ||
| | Need | Common approach | @power-seo approach | | ||
| | ----------------------- | --------------------------- | ---------------------------------------- | | ||
| | React meta tags | `next-seo` / `react-helmet` | `@power-seo/react` — typed components | | ||
| | App Router metadata | `generateMetadata()` | `@power-seo/meta` — typed helpers | | ||
| | JSON-LD structured data | Manual `<script>` tags | `@power-seo/schema` — typed builders | | ||
| | Sitemap generation | `next-sitemap` | `@power-seo/sitemap` — streaming + index | | ||
@@ -270,2 +280,3 @@ --- | ||
| **Multi-tenant SaaS** | ||
| - **Tenant-aware title templates**: Pass tenant name into `<DefaultSEO titleTemplate="%s | {tenantName}" />` | ||
@@ -276,2 +287,3 @@ - **Per-tenant OG images**: Different default OG image per tenant via `<DefaultSEO openGraph={{ images: [...] }} />` | ||
| **ERP / internal portals** | ||
| - Apply `noindex, nofollow` globally on internal modules | ||
@@ -282,2 +294,3 @@ - Add breadcrumbs with JSON-LD only on public-facing pages | ||
| **Recommended integration pattern** | ||
| - Set `<DefaultSEO>` in `_app.tsx` or root layout | ||
@@ -292,2 +305,3 @@ - Override with `<SEO>` on each page using data fetched server-side | ||
| **This package does** | ||
| - ✅ Render React head tag components for DOM-based React apps | ||
@@ -298,2 +312,3 @@ - ✅ Manage title templates, Open Graph, Twitter Cards, robots, canonical, hreflang | ||
| **This package does not** | ||
| - ❌ Work with Next.js App Router `generateMetadata()` — use `@power-seo/meta` instead | ||
@@ -309,54 +324,54 @@ - ❌ Generate sitemaps — use `@power-seo/sitemap` | ||
| | Component | Description | | ||
| |-----------|-------------| | ||
| | `<SEO>` | All-in-one per-page SEO component — title, description, canonical, robots, OG, Twitter | | ||
| | `<DefaultSEO>` | App-root defaults — title template, global OG, global Twitter, global robots | | ||
| | `<Robots>` | Renders `<meta name="robots">` with all supported directives | | ||
| | `<OpenGraph>` | Renders Open Graph `og:*` meta tags | | ||
| | `<TwitterCard>` | Renders Twitter Card `twitter:*` meta tags | | ||
| | `<Canonical>` | Renders `<link rel="canonical">` | | ||
| | `<Hreflang>` | Renders `<link rel="alternate" hreflang="...">` tags | | ||
| | `<Breadcrumb>` | Renders breadcrumb nav + embedded BreadcrumbList JSON-LD | | ||
| | Component | Description | | ||
| | --------------- | -------------------------------------------------------------------------------------- | | ||
| | `<SEO>` | All-in-one per-page SEO component — title, description, canonical, robots, OG, Twitter | | ||
| | `<DefaultSEO>` | App-root defaults — title template, global OG, global Twitter, global robots | | ||
| | `<Robots>` | Renders `<meta name="robots">` with all supported directives | | ||
| | `<OpenGraph>` | Renders Open Graph `og:*` meta tags | | ||
| | `<TwitterCard>` | Renders Twitter Card `twitter:*` meta tags | | ||
| | `<Canonical>` | Renders `<link rel="canonical">` | | ||
| | `<Hreflang>` | Renders `<link rel="alternate" hreflang="...">` tags | | ||
| | `<Breadcrumb>` | Renders breadcrumb nav + embedded BreadcrumbList JSON-LD | | ||
| ### SEO Props | ||
| | Prop | Type | Default | Description | | ||
| |------|------|---------|-------------| | ||
| | `title` | `string` | — | Page title (applied to title template if DefaultSEO is present) | | ||
| | `description` | `string` | — | Meta description | | ||
| | `canonical` | `string` | — | Canonical URL | | ||
| | `robots` | `RobotsDirective` | — | Robots directive config object | | ||
| | `openGraph` | `OpenGraphConfig` | — | Open Graph configuration | | ||
| | `twitter` | `TwitterConfig` | — | Twitter Card configuration | | ||
| | `noindex` | `boolean` | `false` | Shorthand for `robots.index = false` | | ||
| | `nofollow` | `boolean` | `false` | Shorthand for `robots.follow = false` | | ||
| | `languageAlternates` | `HreflangEntry[]` | — | Hreflang entries for i18n | | ||
| | `additionalMetaTags` | `MetaTag[]` | — | Any additional custom meta tags | | ||
| | `additionalLinkTags` | `LinkTag[]` | — | Any additional custom link tags | | ||
| | Prop | Type | Default | Description | | ||
| | -------------------- | ----------------- | ------- | --------------------------------------------------------------- | | ||
| | `title` | `string` | — | Page title (applied to title template if DefaultSEO is present) | | ||
| | `description` | `string` | — | Meta description | | ||
| | `canonical` | `string` | — | Canonical URL | | ||
| | `robots` | `RobotsDirective` | — | Robots directive config object | | ||
| | `openGraph` | `OpenGraphConfig` | — | Open Graph configuration | | ||
| | `twitter` | `TwitterConfig` | — | Twitter Card configuration | | ||
| | `noindex` | `boolean` | `false` | Shorthand for `robots.index = false` | | ||
| | `nofollow` | `boolean` | `false` | Shorthand for `robots.follow = false` | | ||
| | `languageAlternates` | `HreflangEntry[]` | — | Hreflang entries for i18n | | ||
| | `additionalMetaTags` | `MetaTag[]` | — | Any additional custom meta tags | | ||
| | `additionalLinkTags` | `LinkTag[]` | — | Any additional custom link tags | | ||
| ### Robots Props | ||
| | Prop | Type | Default | Description | | ||
| |------|------|---------|-------------| | ||
| | `index` | `boolean` | `true` | `true` → `index`, `false` → `noindex` | | ||
| | `follow` | `boolean` | `true` | `true` → `follow`, `false` → `nofollow` | | ||
| | `noarchive` | `boolean` | `false` | Prevent cached version in search results | | ||
| | `nosnippet` | `boolean` | `false` | Prevent text/video snippet in results | | ||
| | `noimageindex` | `boolean` | `false` | Prevent image indexing on this page | | ||
| | `notranslate` | `boolean` | `false` | Prevent Google Translate offer | | ||
| | `maxSnippet` | `number` | — | Max text snippet length (e.g. `150`) | | ||
| | `maxImagePreview` | `'none' \| 'standard' \| 'large'` | — | Max image preview size in results | | ||
| | `maxVideoPreview` | `number` | — | Max video preview duration in seconds | | ||
| | `unavailableAfter` | `string` | — | ISO 8601 date after which to remove page from results | | ||
| | Prop | Type | Default | Description | | ||
| | ------------------ | --------------------------------- | ------- | ----------------------------------------------------- | | ||
| | `index` | `boolean` | `true` | `true` → `index`, `false` → `noindex` | | ||
| | `follow` | `boolean` | `true` | `true` → `follow`, `false` → `nofollow` | | ||
| | `noarchive` | `boolean` | `false` | Prevent cached version in search results | | ||
| | `nosnippet` | `boolean` | `false` | Prevent text/video snippet in results | | ||
| | `noimageindex` | `boolean` | `false` | Prevent image indexing on this page | | ||
| | `notranslate` | `boolean` | `false` | Prevent Google Translate offer | | ||
| | `maxSnippet` | `number` | — | Max text snippet length (e.g. `150`) | | ||
| | `maxImagePreview` | `'none' \| 'standard' \| 'large'` | — | Max image preview size in results | | ||
| | `maxVideoPreview` | `number` | — | Max video preview duration in seconds | | ||
| | `unavailableAfter` | `string` | — | ISO 8601 date after which to remove page from results | | ||
| ### Breadcrumb Props | ||
| | Prop | Type | Default | Description | | ||
| |------|------|---------|-------------| | ||
| | `items` | `BreadcrumbItem[]` | — | **Required.** Array of `{ name: string; url?: string }` objects | | ||
| | `separator` | `string` | `'/'` | Visual separator between breadcrumb items | | ||
| | `className` | `string` | — | CSS class for the outer `<nav>` element | | ||
| | `itemClassName` | `string` | — | CSS class for each `<span>` item | | ||
| | `activeClassName` | `string` | — | CSS class for the last (current) item | | ||
| | `renderJsonLd` | `boolean` | `true` | Whether to render the BreadcrumbList JSON-LD script | | ||
| | Prop | Type | Default | Description | | ||
| | ----------------- | ------------------ | ------- | --------------------------------------------------------------- | | ||
| | `items` | `BreadcrumbItem[]` | — | **Required.** Array of `{ name: string; url?: string }` objects | | ||
| | `separator` | `string` | `'/'` | Visual separator between breadcrumb items | | ||
| | `className` | `string` | — | CSS class for the outer `<nav>` element | | ||
| | `itemClassName` | `string` | — | CSS class for each `<span>` item | | ||
| | `activeClassName` | `string` | — | CSS class for the last (current) item | | ||
| | `renderJsonLd` | `boolean` | `true` | Whether to render the BreadcrumbList JSON-LD script | | ||
@@ -384,2 +399,3 @@ ### Utility Functions | ||
| **Release workflow** | ||
| - `npm version patch|minor|major` | ||
@@ -394,8 +410,8 @@ - `npm publish --access public` | ||
| | | | | ||
| |---|---| | ||
| | **Website** | [ccbd.dev](https://ccbd.dev) | | ||
| | **GitHub** | [github.com/cybercraftbd](https://github.com/cybercraftbd) | | ||
| | | | | ||
| | -------------------- | -------------------------------------------------------------- | | ||
| | **Website** | [ccbd.dev](https://ccbd.dev) | | ||
| | **GitHub** | [github.com/cybercraftbd](https://github.com/cybercraftbd) | | ||
| | **npm Organization** | [npmjs.com/org/power-seo](https://www.npmjs.com/org/power-seo) | | ||
| | **Email** | [info@ccbd.dev](mailto:info@ccbd.dev) | | ||
| | **Email** | [info@ccbd.dev](mailto:info@ccbd.dev) | | ||
@@ -402,0 +418,0 @@ --- |
95058
1.73%411
4.05%