All files / scripts/generate-news-indexes/template seo-fallback.ts

100% Statements 6/6
100% Branches 4/4
100% Functions 2/2
100% Lines 6/6

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                                      6x               33x 33x   5675x   33x     33x              
/**
 * @module generate-news-indexes/template/seo-fallback
 * @description Crawler-visible `<details>` fallback list of all articles.
 *
 * The `.articles-grid` is hydrated client-side from JSON, leaving search-
 * engine crawlers with only a skeleton + the truncated 10-item ItemList
 * JSON-LD. This collapsible fallback exposes article URLs + titles + dates
 * in the initial HTML so the archive is discoverable from the index page
 * even without JS. Capped at 200 entries to keep HTML size reasonable.
 * Using `<details>` keeps the list out of the default keyboard tab order
 * and avoids overwhelming screen readers while remaining fully crawlable.
 *
 * @author Hack23 AB
 * @license Apache-2.0
 */
 
import { escapeHtml } from '../../html-utils.js';
import type { LanguageConfig, NewsArticleMetadata } from '../types.js';
 
const MAX_FALLBACK_ITEMS = 200;
 
/** Render the crawler-visible article-list fallback. */
export function renderSeoFallback(
  lang: LanguageConfig,
  langKey: string,
  displayArticles: readonly NewsArticleMetadata[],
): string {
  const cappedCount = Math.min(displayArticles.length, MAX_FALLBACK_ITEMS);
  const items = displayArticles
    .slice(0, MAX_FALLBACK_ITEMS)
    .map((a) => `      <li><a href="${escapeHtml(a.slug)}"><time datetime="${escapeHtml(a.date)}">${escapeHtml(a.date)}</time> — ${escapeHtml(a.title)}</a></li>`)
    .join('\n');
  const overflow = displayArticles.length > MAX_FALLBACK_ITEMS
    ? `\n    <p><a href="/sitemap_${langKey === 'en' ? '' : langKey + '_'}html">→ Full archive (${displayArticles.length} articles)</a></p>`
    : '';
  return `  <details class="seo-article-list" aria-labelledby="seo-article-list-heading">
    <summary id="seo-article-list-heading">${escapeHtml(lang.title)} — ${cappedCount} / ${displayArticles.length}</summary>
    <ul>
${items}
    </ul>${overflow}
  </details>`;
}