`get()`-regexens verdi-gren brukte `\s*`, som inkluderer linjeskift. Enhver
key med tom rest-av-linje slukte dermed den neste ikke-tomme linja. Utslaget
var bredere enn list-keys: en tom `title:` returnerte neste keys hele linje
("type: OKR"), og et tomt `description:` dempet okf-checks anbefalt-felt-
advarsel med data som tilhorte en annen key.
Verdi-grenen strammet til `[ \t]*`. Innrykk-ankeret (`^\s*`) er urort -- det
er load-bearing for inject-okr-context.mjs:69s nestede organisasjon:-lesing.
Kontrakt-kommentaren i :13-16 lovet allerede null for list-keys; den er naa
sann i stedet for aa bli rettet ned.
Sju RED-verifiserte tester: (L1) list-key gir null - (L2) tom skalar sluker
ikke neste rot-key - (L3) tom key foran list-blokk - (L4) innrykk-toleransen
bevart, forelder gir null - (L5) tab-separert verdi - (L6) tomt anbefalt felt
demper ikke okf-check-advarselen (rootLevelGet arvet defekten) - (L7)
broedtekst-fallbacken plukker ikke list-verdi fra frontmatter.
(L7) gjor S57s latente begrunnelse maalbar: uttrekket i okf-check.mjs:125 ble
beholdt mot nettopp denne defekten, og naar fm-laget endelig taper for
list-keys er `body = raw` foerste gang en roednende mutasjon.
Mutasjons-verifisert (M1 revert, M3 anker-fjerning, M4 uttrekk-fjerning). M2
viste at (L5) ikke vokter tegnklassen -- .trim() gjor `[ \t]*` og `[ ]*`
ekvivalente -- saa testen er omskrevet til aa paastaa det den faktisk viser.
Suite 321 -> 328.
81 lines
3.7 KiB
JavaScript
81 lines
3.7 KiB
JavaScript
// frontmatter.mjs
|
|
// Delt frontmatter-parse/skrive for okr-pluginen. Zero npm dependencies.
|
|
//
|
|
// Konsoliderer den dupliserte flate `get()`-parseren fra inject-okr-context.mjs
|
|
// (:59-66) og coaching-hook.mjs (:20-27) til EN modul, med to korreksjoner mot
|
|
// den gamle atferden:
|
|
// 1. Linjeanker (`^\s*key:`, multiline) — key matcher kun ved linjestart
|
|
// (modulo innrykk), aldri som substring midt i en annen key/verdi. Bevarer
|
|
// first-match og nestet (innrykket) oppslag (load-bearing: inject:69).
|
|
// 2. Trailing " #kommentar" strippes KUN fra USITERTE verdier. Siterte verdier
|
|
// beholder en intern '#' ("A #B" -> A #B). Retter comment-leak-bugen der
|
|
// `okr_frikoblet_fra_loenn: true # ...` lakk kommentaren inn i verdien.
|
|
//
|
|
// Verdi-delen matches med `[ \t]*`, ALDRI `\s*`: `\s` inkluderer linjeskift, saa
|
|
// en verdi-`\s*` lot enhver key med tom rest-av-linje sluke den neste ikke-tomme
|
|
// linja. Utslaget var ikke begrenset til list-keys -- en tom `title:` returnerte
|
|
// neste keys hele linje ("type: OKR"), og et tomt `description:` dempet
|
|
// okf-checks anbefalt-felt-advarsel med data som tilhorte en annen key.
|
|
// De to `\s`-ene har ULIK jobb: innrykk-anker (bevart) vs. verdi-avgrensning
|
|
// (strammet). Tester: (L1)-(L4) + (L6). Bytt aldri verdi-grenen tilbake til `\s*`.
|
|
// `\t` i klassen er intensjons-dokumentasjon, ikke atferd: .trim() under gjor
|
|
// `[ \t]*` og `[ ]*` ekvivalente (mutasjons-verifisert). Det som BAERER fiksen er
|
|
// at linjeskift er utenfor klassen.
|
|
//
|
|
// Tolererer fler-linje OKF-list-verdier (f.eks. `tags:`) uten krasj: get() paa
|
|
// en list-key returnerer null (rest-av-linja er tom); list-elementer paa
|
|
// foelgende linjer konsumeres aldri (ingen konsument leser tre-filenes `tags`).
|
|
|
|
const FM_RE = /^---\n([\s\S]*?)\n---/;
|
|
|
|
export function parseFrontmatter(content) {
|
|
// B2: toler UTF-8 BOM foran forste fence + CRLF-linjeskift (Windows-produserte
|
|
// filer) -- ellers bommer FM_RE og fila rapporteres falskt som "mangler type".
|
|
const normalized = String(content).replace(/^\uFEFF/, '').replace(/\r\n/g, '\n');
|
|
const match = normalized.match(FM_RE);
|
|
const raw = match ? match[1] : null;
|
|
|
|
const get = (key) => {
|
|
if (raw === null) return null;
|
|
const m = raw.match(new RegExp(`^\\s*${key}:[ \\t]*(.*)$`, 'm'));
|
|
if (!m) return null;
|
|
let v = m[1].trim();
|
|
if (v === '') return null;
|
|
const q = v[0];
|
|
if (q === '"' || q === "'") {
|
|
const end = v.indexOf(q, 1);
|
|
if (end !== -1) return v.slice(1, end); // intern '#' bevart
|
|
v = v.slice(1); // uavsluttet quote: fall tilbake til resten
|
|
} else {
|
|
v = v.replace(/\s+#.*$/, '').trim(); // usitert: strip trailing kommentar
|
|
}
|
|
return v === '' ? null : v;
|
|
};
|
|
|
|
return { raw, get };
|
|
}
|
|
|
|
// Siter naar verdien inneholder '#'/':' eller har kant-whitespace, slik at
|
|
// round-trip via parseFrontmatter bevarer den eksakt (jf. siter-#-regelen).
|
|
const quoteIfNeeded = (s) =>
|
|
/[#:]/.test(s) || /^\s|\s$/.test(s) || /^["']/.test(s) ? JSON.stringify(s) : s;
|
|
|
|
export function writeFrontmatter(fields) {
|
|
const lines = ['---'];
|
|
for (const [key, value] of Object.entries(fields)) {
|
|
// Additiv array-gren (Step 3): emit OKF multi-linje list-verdi (f.eks.
|
|
// `tags`) som `key:\n - item`. Lese-siden er uendret -- parseFrontmatter.
|
|
// get() returnerer fortsatt null for list-keys. Skalar-grenen under er
|
|
// bevart bit-for-bit (kun loftet ut til quoteIfNeeded, samme regel).
|
|
if (Array.isArray(value)) {
|
|
lines.push(`${key}:`);
|
|
for (const item of value) {
|
|
lines.push(` - ${quoteIfNeeded(String(item))}`);
|
|
}
|
|
continue;
|
|
}
|
|
lines.push(`${key}: ${quoteIfNeeded(String(value))}`);
|
|
}
|
|
lines.push('---');
|
|
return lines.join('\n') + '\n';
|
|
}
|