UNPKG

@ai-growth/nextjs

Version:

Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering

133 lines (132 loc) 6.37 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BasicSEO = exports.StructuredDataOnly = exports.SafeSEOHead = exports.SEOHead = void 0; exports.useSEOData = useSEOData; const jsx_runtime_1 = require("react/jsx-runtime"); /** * @fileoverview SEO Head Component * * This component provides comprehensive SEO functionality including * meta tags, Open Graph, Twitter Cards, and JSON-LD structured data. */ const react_1 = __importDefault(require("react")); const head_1 = __importDefault(require("next/head")); const seo_1 = require("../utils/seo"); const structured_data_1 = require("../utils/seo/structured-data"); /** * SEO Head component that generates comprehensive SEO metadata * * @example * ```tsx * <SEOHead * content={post} * siteSettings={siteSettings} * template="article" * url="https://example.com/blog/post-slug" * /> * ``` */ const SEOHead = (props) => { // Process SEO data and generate results const seoResult = (0, seo_1.processSEO)(props); // Sanitize SEO data for optimal display const sanitizedSEO = (0, seo_1.sanitizeSEO)({ title: seoResult.title, description: seoResult.description, canonical: seoResult.canonical, keywords: props.seo?.keywords || [], noIndex: props.seo?.noIndex || false, noFollow: props.seo?.noFollow || false }); return ((0, jsx_runtime_1.jsxs)(head_1.default, { children: [renderMetaTags(seoResult.metaTags), (0, jsx_runtime_1.jsx)("title", { children: sanitizedSEO.title }), sanitizedSEO.canonical && ((0, jsx_runtime_1.jsx)("link", { rel: "canonical", href: sanitizedSEO.canonical })), props.seo?.alternateLanguages?.map((lang, index) => ((0, jsx_runtime_1.jsx)("link", { rel: "alternate", hrefLang: `${lang.language}${lang.region ? `-${lang.region}` : ''}`, href: lang.url }, index))), seoResult.structuredData.map((data, index) => ((0, jsx_runtime_1.jsx)("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: (0, structured_data_1.escapeJsonLD)(data) } }, index))), (0, jsx_runtime_1.jsx)("link", { rel: "dns-prefetch", href: "//fonts.googleapis.com" }), (0, jsx_runtime_1.jsx)("link", { rel: "dns-prefetch", href: "//cdn.sanity.io" }), (0, jsx_runtime_1.jsx)("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" })] })); }; exports.SEOHead = SEOHead; /** * Render meta tags from the generated list * * @param metaTags - Array of meta tags to render * @returns JSX elements for meta tags */ function renderMetaTags(metaTags) { return metaTags.map((tag, index) => { const key = `meta-${index}`; // Handle charset meta tag if (tag.charset) { return (0, jsx_runtime_1.jsx)("meta", { charSet: tag.charset }, key); } // Handle http-equiv meta tag if (tag.httpEquiv) { return (0, jsx_runtime_1.jsx)("meta", { httpEquiv: tag.httpEquiv, content: tag.content }, key); } // Handle property meta tag (Open Graph) if (tag.property) { return (0, jsx_runtime_1.jsx)("meta", { property: tag.property, content: tag.content }, key); } // Handle name meta tag (standard and Twitter) if (tag.name) { return (0, jsx_runtime_1.jsx)("meta", { name: tag.name, content: tag.content }, key); } // Fallback for any other meta tag type return (0, jsx_runtime_1.jsx)("meta", { content: tag.content }, key); }); } /** * SEO Head component with error boundary */ const SafeSEOHead = (props) => { try { return (0, jsx_runtime_1.jsx)(exports.SEOHead, { ...props }); } catch (error) { console.error('SEO Head error:', error); // Fallback SEO with basic meta tags return ((0, jsx_runtime_1.jsxs)(head_1.default, { children: [(0, jsx_runtime_1.jsx)("title", { children: props.content?.title || props.siteSettings?.title || 'AI Growth' }), (0, jsx_runtime_1.jsx)("meta", { name: "description", content: props.content?.excerpt || props.siteSettings?.description || '' }), (0, jsx_runtime_1.jsx)("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), (0, jsx_runtime_1.jsx)("meta", { charSet: "utf-8" })] })); } }; exports.SafeSEOHead = SafeSEOHead; /** * Hook for accessing SEO data without rendering * * @param props - SEO Head props * @returns Processed SEO result */ function useSEOData(props) { return react_1.default.useMemo(() => { try { return (0, seo_1.processSEO)(props); } catch (error) { console.error('SEO processing error:', error); return { metaTags: [], structuredData: [], title: props.content?.title || 'AI Growth', description: props.content?.excerpt || '', canonical: props.url || '', image: undefined }; } }, [props]); } /** * Utility component for JSON-LD structured data only * * @param props - Structured data props */ const StructuredDataOnly = ({ data }) => ((0, jsx_runtime_1.jsx)(head_1.default, { children: data.map((item, index) => ((0, jsx_runtime_1.jsx)("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: (0, structured_data_1.escapeJsonLD)(item) } }, index))) })); exports.StructuredDataOnly = StructuredDataOnly; /** * Utility component for basic meta tags only * * @param props - Basic meta props */ const BasicSEO = ({ title, description, canonical, noIndex }) => ((0, jsx_runtime_1.jsxs)(head_1.default, { children: [(0, jsx_runtime_1.jsx)("title", { children: title }), description && (0, jsx_runtime_1.jsx)("meta", { name: "description", content: description }), canonical && (0, jsx_runtime_1.jsx)("link", { rel: "canonical", href: canonical }), (0, jsx_runtime_1.jsx)("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), (0, jsx_runtime_1.jsx)("meta", { charSet: "utf-8" }), noIndex && (0, jsx_runtime_1.jsx)("meta", { name: "robots", content: "noindex, nofollow" })] })); exports.BasicSEO = BasicSEO; exports.default = exports.SEOHead;