const collectFrontmatter = (raw) => { const lines = raw.split(/\r?\n/); const meta = {}; let cursor = 0; if (lines[0]?.trim() === '---') { cursor = 1; for (; cursor < lines.length; cursor += 1) { const rawLine = lines[cursor]; const line = rawLine.trim(); if (line === '---') { cursor += 1; break; } if (!line) continue; if (/^\s+/.test(rawLine)) continue; if (!line.includes(':')) continue; const [key, ...rest] = line.split(':'); meta[key.trim()] = rest.join(':').trim(); } } const body = lines.slice(cursor).join('\n').trim(); return { meta, body }; }; const extractTitle = (body) => { const titleLine = body.split(/\r?\n/).find((line) => line.startsWith('# ')); return titleLine ? titleLine.replace(/^#\s+/, '').trim() : ''; }; const extractExcerpt = (body) => { const lines = body.split(/\r?\n/); for (const line of lines) { if (!line.trim()) continue; if (line.startsWith('#')) continue; return line.trim(); } return ''; }; const allowedTags = new Set(['일상', '개발', '공부', '아이디어', '기타']); const normalizeTags = (value) => { if (!value) return []; const mapped = value .split(',') .map((tag) => tag.trim()) .filter(Boolean) .map((tag) => { const lower = tag.toLowerCase(); if (lower === 'diary' || lower === 'dailylog') return '일상'; if (lower === 'dev' || lower === 'development') return '개발'; if (lower === 'study') return '공부'; if (lower === 'idea' || lower === 'ideas') return '아이디어'; return tag; }); const normalized = mapped.map((tag) => (allowedTags.has(tag) ? tag : '기타')); return Array.from(new Set(normalized)); }; const normalizeTitle = (slug) => slug .replace(/-/g, ' ') .replace(/\b\w/g, (letter) => letter.toUpperCase()); const inferTagFromPath = (path) => { const match = path.match(/\/blog\/([^/]+)\//); if (!match) return ''; const folder = match[1]; if (folder === 'daily') return '일상'; if (folder === 'dev') return '개발'; if (folder === 'study') return '공부'; if (folder === 'ideas') return '아이디어'; return '기타'; }; const inferDateFromSlug = (slug) => { const match = slug.match(/\d{4}-\d{2}-\d{2}/); return match ? match[0] : ''; }; export const getBlogPosts = () => { const modules = import.meta.glob('/src/content/blog/**/*.md', { as: 'raw', eager: true, }); const posts = Object.entries(modules).map(([path, raw]) => { const slug = path.split('/').pop().replace(/\.md$/, ''); const { meta, body } = collectFrontmatter(raw); const inferredTag = inferTagFromPath(path); const title = meta.title || extractTitle(body) || normalizeTitle(slug); const excerpt = meta.excerpt || meta.description || extractExcerpt(body); const date = meta.date || inferDateFromSlug(slug); const baseTags = normalizeTags(meta.tags); const categoryTags = normalizeTags(meta.category); const tags = Array.from( new Set([ ...baseTags, ...categoryTags, ...(inferredTag ? [inferredTag] : []), ]) ); return { slug, title, excerpt, date, tags, body, }; }); return posts.sort((a, b) => { const aDate = Date.parse(a.date || '') || 0; const bDate = Date.parse(b.date || '') || 0; return bDate - aDate; }); };