All files / src/lib/seo seo.ts

100% Statements 10/10
72.22% Branches 13/18
100% Functions 2/2
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92                      1x                                       3x 3x     3x       3x       3x         3x       3x                                               1x                 3x            
import type { Metadata } from 'next';
import { getLocaleMessages } from '@/lib/locale/locale';
import type { AuthMessages } from '../../types/common';
 
/**
 * Helper to build SEO metadata for any page using localized messages.
 *
 * @param locale    - locale string read from the cookie
 * @param namespace - the key in the messages object (e.g. 'seo', 'auth', 'user')
 * @param opts      - keys to look up and fallback values
 */
export const getMetadata = async (
  locale: string,
  namespace: string,
  opts: {
    titleKey: string;
    descriptionKey: string;
    fallbackTitle: string;
    fallbackDescription: string;
    titleTemplate?: string; // Optional template like "%s | Cookbook"
    keywordsKey?: string;
    fallbackKeywords?: string;
    robots?: {
      index?: boolean;
      follow?: boolean;
    };
    openGraph?: {
      type?: 'website' | 'article';
    };
  },
): Promise<Metadata> => {
  const messages = await getLocaleMessages(locale);
  const data = (messages[namespace] ?? {}) as Record<string, string>;
 
  const title =
    typeof data[opts.titleKey] === 'string'
      ? data[opts.titleKey]
      : opts.fallbackTitle;
  const description =
    typeof data[opts.descriptionKey] === 'string'
      ? data[opts.descriptionKey]
      : opts.fallbackDescription;
 
  const finalTitle = opts.titleTemplate
    ? opts.titleTemplate.replace('%s', title)
    : `${title} | Cookbook`;
 
  const keywords =
    opts.keywordsKey && typeof data[opts.keywordsKey] === 'string'
      ? data[opts.keywordsKey]
      : opts.fallbackKeywords;
 
  return {
    title: finalTitle,
    description,
    ...(keywords === undefined ? {} : { keywords }),
    ...(opts.robots === undefined ? {} : { robots: opts.robots }),
    openGraph: {
      title: finalTitle,
      description,
      type: opts.openGraph?.type ?? 'website',
    },
    twitter: {
      card: 'summary',
      title: finalTitle,
      description,
    },
  };
};
 
/**
 * Helper to build SEO metadata for auth‑related pages.
 *
 * @param locale - locale string read from the cookie
 * @param opts   - keys to look up in the `auth` namespace and fallback values
 */
export const getAuthMetadata = async (
  locale: string,
  opts: {
    titleKey: keyof AuthMessages;
    descriptionKey: keyof AuthMessages;
    fallbackTitle: string;
    fallbackDescription: string;
  },
): Promise<Metadata> => {
  return getMetadata(locale, 'auth', {
    ...opts,
    titleKey: String(opts.titleKey),
    descriptionKey: String(opts.descriptionKey),
  });
};