Scrum4Me/lib/product-doc-frontmatter.ts
Madhura68 afafbca855 feat(PBI-96/T-1060): add frontmatter parser + serializer (P2-fix)
- lib/product-doc-parser.ts: parseProductDocMd(md) → {ok, frontmatter, body}
  | {ok:false, errors[]} met line-info bij YAML-fouten. Pattern gespiegeld
  uit lib/idea-plan-parser.ts.
- lib/product-doc-frontmatter.ts: setProductDocFrontmatterFields(md, patch)
  laat de server `last_updated` server-side normaliseren (P2-review-fix
  uit docs/recommendations/product-docs-storage-system-review-2026-05-16).
  Gebruikt yaml.parseDocument om field-ordering best-effort te behouden.
- todayIsoDate() helper voor 'yyyy-mm-dd' string.
- __tests__: 19 nieuwe tests groen — parse-success/fail-paden, en
  expliciete P2-coverage (vervangen + toevoegen last_updated, behoud
  overige velden + body).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 11:42:03 +02:00

59 lines
2 KiB
TypeScript

// Server-side serializer die individuele frontmatter-velden bijwerkt in
// een al-gevalideerde markdown-doc. P2-review-fix uit
// docs/recommendations/product-docs-storage-system-review-2026-05-16.md
// (last_updated moet door de server worden gezet, niet door de user).
//
// Caller MOET parseProductDocMd al hebben aangeroepen voor pre-validatie
// — deze functie throwed bij parse-fouten.
import { parseDocument } from 'yaml'
const FRONTMATTER_RE =
/^(---\r?\n)([\s\S]*?)(\r?\n---\r?\n?)([\s\S]*)$/
/**
* Mutates de YAML-frontmatter van `md` met de gegeven `patch`-keys
* (bv. `{ last_updated: '2026-05-16' }`) en geeft de nieuwe markdown
* terug. Behoudt body en frontmatter-delimiters; overige velden blijven
* staan (best-effort op ordering en whitespace via yaml-lib).
*/
export function setProductDocFrontmatterFields(
md: string,
patch: Record<string, unknown>,
): string {
const match = md.match(FRONTMATTER_RE)
if (!match) {
throw new Error(
'setProductDocFrontmatterFields: input mist yaml-frontmatter (geen `---` opener gevonden)',
)
}
const [, openMarker, frontmatterRaw, closeMarker, body] = match
const doc = parseDocument(frontmatterRaw)
if (doc.errors.length > 0) {
throw new Error(
`setProductDocFrontmatterFields: yaml parse-error op regel ${
doc.errors[0].linePos?.[0]?.line ?? '?'
}: ${doc.errors[0].message}`,
)
}
for (const [key, value] of Object.entries(patch)) {
doc.set(key, value)
}
// yaml.Document.toString() voegt vaak een trailing newline toe —
// strippen voorkomt dubbele newlines vóór de afsluitende `---`.
const newFrontmatter = doc.toString().replace(/\r?\n$/, '')
return `${openMarker}${newFrontmatter}${closeMarker}${body}`
}
/**
* ISO-date (yyyy-mm-dd) van vandaag — handige helper voor de server om
* `last_updated` mee te zetten bij elke save.
*/
export function todayIsoDate(now: Date = new Date()): string {
return now.toISOString().slice(0, 10)
}