@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
JavaScript
;
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;