parseDocument.js (2460B)
1 import { getSettings, parse } from 'orga'; 2 import { normalizeOrgContent } from './normalize.js'; 3 4 function headlineTitle(headline) { 5 const parts = []; 6 for (const child of headline.children ?? []) { 7 if (child.type === 'text' && child.value) { 8 parts.push(child.value); 9 } 10 } 11 return parts.join('').trim() || 'Untitled section'; 12 } 13 14 function headlineLevel(headline) { 15 const stars = headline.children?.find((child) => child.type === 'stars'); 16 if (stars?.level) return stars.level; 17 if (stars?.value) return stars.value.length; 18 return 1; 19 } 20 21 function headlineTodo(headline) { 22 const todoNode = headline.children?.find((child) => child.type === 'todo'); 23 return todoNode?.keyword ?? null; 24 } 25 26 function headlineTags(headline) { 27 const tagsNode = headline.children?.find((child) => child.type === 'tags'); 28 if (!tagsNode?.tags?.length) return []; 29 return tagsNode.tags; 30 } 31 32 function walkSection(section, headings) { 33 if (!section?.children) return; 34 35 for (const child of section.children) { 36 if (child.type === 'headline') { 37 headings.push({ 38 level: headlineLevel(child), 39 title: headlineTitle(child), 40 todo: headlineTodo(child), 41 tags: headlineTags(child), 42 line: child.position?.start?.line ?? 1, 43 }); 44 } 45 if (child.type === 'section') { 46 walkSection(child, headings); 47 } 48 } 49 } 50 51 export function parseOrgDocument(content) { 52 if (!content?.trim()) { 53 return { 54 keywords: {}, 55 headings: [], 56 title: null, 57 author: null, 58 todoKeywords: ['TODO', 'DONE'], 59 }; 60 } 61 62 const normalized = normalizeOrgContent(content); 63 const settings = getSettings(normalized); 64 const doc = parse(normalized); 65 const headings = []; 66 67 for (const child of doc.children ?? []) { 68 if (child.type === 'section') { 69 walkSection(child, headings); 70 } 71 } 72 73 const titleSetting = settings.title; 74 const authorSetting = settings.author; 75 const todoSetting = settings.todo; 76 77 let todoKeywords = ['TODO', 'DONE']; 78 if (typeof todoSetting === 'string') { 79 todoKeywords = todoSetting.split(/\s+/).filter(Boolean); 80 } else if (Array.isArray(todoSetting)) { 81 todoKeywords = todoSetting.flatMap((entry) => entry.split(/\s+/)).filter(Boolean); 82 } 83 84 return { 85 keywords: settings, 86 headings, 87 title: typeof titleSetting === 'string' ? titleSetting : null, 88 author: typeof authorSetting === 'string' ? authorSetting : null, 89 todoKeywords, 90 }; 91 }