#!/usr/bin/env node /** * SOLARIA STATIC SITE GENERATOR * * Pure recursion. No pain. Just joy. * * Input: index.json * Output: static HTML files in /dist */ import { promises as fs } from 'fs'; import path from 'path'; const ROOT = 'public'; const OUTPUT = 'dist'; // Simple markdown to HTML converter function mdToHtml(md) { if (!md) return ''; // Remove frontmatter if present md = md.replace(/^---[\s\S]*?---/, ''); return md // Headers .replace(/^### (.*$)/gim, '

$1

') .replace(/^## (.*$)/gim, '

$1

') .replace(/^# (.*$)/gim, '

$1

') // Bold/Italic .replace(/\*\*(.*)\*\*/gim, '$1') .replace(/\*(.*)\*/gim, '$1') // Links .replace(/\[(.*?)\]\((.*?)\)/gim, '$1') // Blockquotes .replace(/^> (.*$)/gim, '
$1
') // Horizontal rules .replace(/^---$/gim, '
') // Line breaks .replace(/\n/g, '
'); } // Extract content from fieldnote file async function readFieldnote(filePath) { // filePath already includes 'fieldnotes/' prefix const fullPath = path.join(ROOT, filePath); try { const content = await fs.readFile(fullPath, 'utf8'); const body = content.replace(/^---[\s\S]*?---/, ''); return mdToHtml(body.trim()); } catch { return ''; } } // Generate index.html function generateIndex(data) { const pinned = data.flat .filter(f => f.order > 0) .sort((a, b) => a.order - b.order); const others = data.flat .filter(f => f.order === 0 && !f.isIndex) .sort((a, b) => new Date(b.originalDate) - new Date(a.originalDate)); const pinnedHTML = pinned.map(f => `

${f.title}

${f.originalDate} • ${f.authors.join(', ')}

${f.excerpt ? f.excerpt.substring(0, 200) : ''}

`).join('\n'); return ` The Fold Within Earth

The Fold Within Earth

Recursive Coherence Theory. Human-AI Co-evolution.

Featured

${pinnedHTML}

Recent

`; } // Generate fieldnote page function generateFieldnoteHtml(file, content) { return ` ${file.title}

${file.title}

${file.originalDate || '—'} • ${file.authors.join(', ')}

${content}
`; } // MAIN async function main() { console.log('🔮 Solaria Static Site Generator'); console.log('=================================\n'); // Read index data const indexData = JSON.parse(await fs.readFile(path.join(ROOT, 'index.json'), 'utf8')); console.log('📄 Loaded', indexData.flat.length, 'items'); console.log('📌 Pinned:', indexData.flat.filter(f => f.order > 0).length); // Ensure output directory await fs.mkdir(OUTPUT, { recursive: true }); await fs.mkdir(path.join(OUTPUT, 'fieldnotes'), { recursive: true }); // Copy CSS await fs.copyFile(path.join(ROOT, 'style.css'), path.join(OUTPUT, 'style.css')); console.log('📋 style.css copied'); // Generate index.html const indexHTML = generateIndex(indexData); await fs.writeFile(path.join(OUTPUT, 'index.html'), indexHTML); console.log('✅ index.html'); // Generate fieldnote pages const fieldnotes = indexData.flat.filter(f => !f.isIndex && f.ext === '.md'); console.log('\n📝 Generating', fieldnotes.length, 'fieldnotes...'); for (const file of fieldnotes) { const content = await readFieldnote(file.path); if (!content) { console.log('⚠️ Empty content for:', file.name); } const html = generateFieldnoteHtml(file, content); const outPath = path.join(OUTPUT, 'fieldnotes', file.name.replace('.md', '.html')); await fs.writeFile(outPath, html); } console.log('✅', fieldnotes.length, 'fieldnote pages'); console.log('\n🎉 Done! Static site in /dist'); } main().catch(console.error);