All files / utils news-metadata.ts

100% Statements 23/23
92.3% Branches 12/13
100% Functions 5/5
100% Lines 22/22

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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110                                              1x                 6x 6x   6x 6x 6x 6x 6x 6x                       6x   6x                               3x 3x 1x   3x                       3x 1x     2x 2x                           1x 1x 1x    
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
// SPDX-License-Identifier: Apache-2.0
 
/**
 * @module Utils/NewsMetadata
 * @description News metadata database management.
 * Maintains a JSON database of article metadata that can be loaded
 * by client-side JavaScript, removing the need to edit all index HTML
 * files when adding new articles.
 */
 
import fs from 'fs';
import path from 'path';
import { NEWS_DIR } from '../constants/config.js';
import {
  getNewsArticles,
  parseArticleFilename,
  formatSlug,
  extractArticleMeta,
} from './file-utils.js';
import type { ArticleMetadataEntry, NewsMetadataDatabase } from '../types/index.js';
 
/** Default path for the metadata database file */
const METADATA_DB_PATH = path.join(NEWS_DIR, 'articles-metadata.json');
 
/**
 * Build metadata database from news article files
 *
 * @param newsDir - News directory path
 * @returns News metadata database object
 */
export function buildMetadataDatabase(newsDir: string = NEWS_DIR): NewsMetadataDatabase {
  const articleFiles = getNewsArticles(newsDir);
  const articles: ArticleMetadataEntry[] = [];
 
  for (const filename of articleFiles) {
    const parsed = parseArticleFilename(filename);
    Eif (parsed) {
      const filepath = path.join(newsDir, filename);
      const meta = extractArticleMeta(filepath);
      articles.push({
        filename: parsed.filename,
        date: parsed.date,
        slug: parsed.slug,
        lang: parsed.lang,
        title: meta.title || formatSlug(parsed.slug),
        description: meta.description,
      });
    }
  }
 
  // Sort by date (newest first)
  articles.sort((a, b) => b.date.localeCompare(a.date));
 
  return {
    lastUpdated: new Date().toISOString(),
    articles,
  };
}
 
/**
 * Write metadata database to JSON file
 *
 * @param database - Metadata database to write
 * @param outputPath - Output file path (defaults to news/articles-metadata.json)
 */
export function writeMetadataDatabase(
  database: NewsMetadataDatabase,
  outputPath: string = METADATA_DB_PATH
): void {
  const dir = path.dirname(outputPath);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
  fs.writeFileSync(outputPath, JSON.stringify(database, null, 2), 'utf-8');
}
 
/**
 * Read metadata database from JSON file
 *
 * @param inputPath - Input file path (defaults to news/articles-metadata.json)
 * @returns Metadata database or null if file doesn't exist
 */
export function readMetadataDatabase(
  inputPath: string = METADATA_DB_PATH
): NewsMetadataDatabase | null {
  if (!fs.existsSync(inputPath)) {
    return null;
  }
 
  const content = fs.readFileSync(inputPath, 'utf-8');
  return JSON.parse(content) as NewsMetadataDatabase;
}
 
/**
 * Update metadata database by rescanning the news directory
 *
 * @param newsDir - News directory to scan
 * @param outputPath - Output path for metadata JSON
 * @returns Updated metadata database
 */
export function updateMetadataDatabase(
  newsDir: string = NEWS_DIR,
  outputPath: string = METADATA_DB_PATH
): NewsMetadataDatabase {
  const database = buildMetadataDatabase(newsDir);
  writeMetadataDatabase(database, outputPath);
  return database;
}