UNPKG

aubade

Version:

filesystem-based content processor

67 lines (66 loc) 2.47 kB
export function parse(raw, memo = {}) { raw = raw.trim(); if (!/[:\-\[\]|#]/gm.test(raw)) return coerce(raw); if (raw.length > 2) { const start = raw[0]; const end = raw[raw.length - 1]; if (start === end && (end === '"' || end === "'")) return raw.slice(1, -1); if (start === '[' && end === ']') { const result = []; let current = ''; let quoted = null; let escaped = false; for (const char of raw.slice(1, -1)) { quoted = char === quoted ? null : char === '"' || char === "'" ? char : quoted; escaped = !escaped && char === '\\'; if (!quoted && char === ',') result.push(coerce(current)); current = quoted || escaped || char !== ',' ? current + char : ''; } if (current) result.push(coerce(current)); return result; } } const PATTERN = /(^[^:\s]+):(?!\/)\r?\n?([\s\S]*?(?=^\S)|[\s\S]*$)/gm; let match; while ((match = PATTERN.exec(raw))) { const [, key, value] = match; const data = parse(outdent(value), memo[key]); if (data === null || Array.isArray(data) || typeof data !== 'object') memo[key] = data; else memo[key] = { ...memo[key], ...data }; } if (Object.keys(memo).length) return memo; const cleaned = raw.replace(/#.*$/gm, '').trim(); switch (cleaned[0]) { case '-': { const sequence = cleaned.split(/^- /gm).filter((v) => v); const tabbed = sequence.map((v) => v.replace(/\n( +)/g, (_, s) => '\n' + '\t'.repeat(s.length / 2))); return tabbed.map((v) => parse(outdent(` ${v}`))); } case '|': { return outdent(cleaned.slice(1).replace('\n', '')); } default: { return coerce(cleaned); } } } function coerce(u) { const v = u.trim(); // argument can be passed as-is const map = { true: true, false: false, null: null }; if (v in map) return map[v]; // if (!Number.isNaN(Number(v))) return Number(v); return /^(".*"|'.*')$/.test(v) ? v.slice(1, -1) : v; } function outdent(input) { const lines = input.split(/\r?\n/).filter((l) => l.trim()); const { length } = (/^\s*/.exec(lines[0]) || [''])[0]; return lines.map((l) => l.slice(length)).join('\n'); }