Minimal YAML-ish parser. Same shape as custom-commands.ts for consistency.
(fm: string)
| 147 | |
| 148 | /** Minimal YAML-ish parser. Same shape as custom-commands.ts for consistency. */ |
| 149 | function parseFrontmatter(fm: string): Record<string, string | string[]> { |
| 150 | const out: Record<string, string | string[]> = {}; |
| 151 | const lines = fm.split('\n'); |
| 152 | let i = 0; |
| 153 | while (i < lines.length) { |
| 154 | const line = lines[i]!; |
| 155 | if (/^\s*(#.*)?$/.test(line)) { i++; continue; } |
| 156 | const m = line.match(/^([a-zA-Z][\w-]*)\s*:\s*(.*)$/); |
| 157 | if (!m) { i++; continue; } |
| 158 | const key = m[1]!.toLowerCase(); |
| 159 | const rawValue = m[2]!.trim(); |
| 160 | |
| 161 | if (rawValue === '') { |
| 162 | const items: string[] = []; |
| 163 | i++; |
| 164 | while (i < lines.length && /^\s+-\s+/.test(lines[i]!)) { |
| 165 | items.push(stripQuotes(lines[i]!.replace(/^\s+-\s+/, '').trim())); |
| 166 | i++; |
| 167 | } |
| 168 | out[key] = items; |
| 169 | } else if (rawValue.startsWith('[') && rawValue.endsWith(']')) { |
| 170 | out[key] = rawValue.slice(1, -1).split(',').map(s => stripQuotes(s.trim())).filter(Boolean); |
| 171 | i++; |
| 172 | } else { |
| 173 | out[key] = stripQuotes(rawValue); |
| 174 | i++; |
| 175 | } |
| 176 | } |
| 177 | return out; |
| 178 | } |
| 179 | |
| 180 | function asStringArray(v: unknown): string[] | undefined { |
| 181 | if (!v) return undefined; |
no test coverage detected