import { readFileSync, readdirSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { staticRoutes } from './routes.js';
const PUBLIC = 'public';
function readJson(path) {
try {
return JSON.parse(readFileSync(path, 'utf8'));
} catch {
return null;
}
}
function blogRoutes() {
const posts = readJson(join(PUBLIC, 'posts', 'posts.json')) || [];
const out = new Set();
for (const entry of posts) {
if (!entry?.slug) continue;
if (entry.series?.posts?.length) {
out.add(`/blog/series/${entry.slug}`);
for (const ep of entry.series.posts) {
if (ep?.slug) out.add(`/blog/series/${entry.slug}/${ep.slug}`);
}
} else {
out.add(`/blog/${entry.slug}`);
}
}
return out;
}
function logsRoutes() {
const out = new Set();
const root = join(PUBLIC, 'logs');
if (!existsSync(root)) return out;
for (const category of readdirSync(root, { withFileTypes: true })) {
if (!category.isDirectory()) continue;
const dir = join(root, category.name);
for (const file of readdirSync(dir)) {
if (file.endsWith('.txt')) {
out.add(`/logs/${category.name}/${file.slice(0, -4)}`);
}
}
// Some categories (e.g. quote) keep their entries inline in the
// category piml with no per-slug .txt pull slugs from there too.
const pimlPath = join(dir, `${category.name}.piml`);
if (existsSync(pimlPath)) {
const text = readFileSync(pimlPath, 'utf8');
for (const line of text.split(/\r?\n/)) {
const m = line.match(/^\s*\(slug\)\s*(\S+)/);
if (m) out.add(`/logs/${category.name}/${m[1]}`);
}
}
}
return out;
}
function storyBookRoutes() {
const out = new Set();
const files = ['books_en.piml', 'books_tr.piml']
.map((f) => join(PUBLIC, 'stories', f))
.filter(existsSync);
const bookIds = new Set();
const bookEpisodes = new Map();
for (const f of files) {
const text = readFileSync(f, 'utf8');
let currentBook = null;
for (const line of text.split(/\r?\n/)) {
const bookMatch = line.match(/\(bookId\)\s*(\S+)/);
if (bookMatch) {
currentBook = bookMatch[1];
bookIds.add(currentBook);
if (!bookEpisodes.has(currentBook)) bookEpisodes.set(currentBook, new Set());
continue;
}
const epMatch = line.match(/^\s*\(id\)\s*(\S+)/);
if (epMatch && currentBook) {
bookEpisodes.get(currentBook).add(epMatch[1]);
}
}
}
for (const id of bookIds) out.add(`/stories/books/${id}`);
for (const [book, eps] of bookEpisodes) {
for (const ep of eps) out.add(`/stories/books/${book}/pages/${ep}`);
}
return out;
}
/**
* /demystify is driven entirely by text files: public/demystify/index.txt
* registers the collections, and each collection's index.txt registers its
* entries. Reading them here means a new collection or entry is prerendered
* and sitemapped without touching this file.
*/
function demystifyRoutes() {
const out = new Set();
const root = join(PUBLIC, 'demystify');
const registry = join(root, 'index.txt');
if (!existsSync(registry)) return out;
const idsIn = (file) => {
const ids = [];
for (const line of readFileSync(file, 'utf8').split(/\r?\n/)) {
const m = line.match(/^id:\s*(\S+)/);
if (m) ids.push(m[1]);
}
return ids;
};
out.add('/demystify');
for (const collection of idsIn(registry)) {
const index = join(root, collection, 'index.txt');
if (!existsSync(index)) continue;
out.add(`/demystify/${collection}`);
for (const entry of idsIn(index)) {
out.add(`/demystify/${collection}/${entry}`);
}
}
return out;
}
export function discoverAllRoutes() {
const all = new Set(staticRoutes);
for (const r of blogRoutes()) all.add(r);
for (const r of logsRoutes()) all.add(r);
for (const r of storyBookRoutes()) all.add(r);
for (const r of demystifyRoutes()) all.add(r);
return Array.from(all);
}