···3636 with _ -> None
3737 else None
38383939+(** Normalize a slug to match Jekyll's slug_of_string behavior:
4040+ map all non-alphanumeric characters to hyphens and lowercase. *)
4141+let normalize_slug s =
4242+ let mapped = String.map (fun c ->
4343+ match c with
4444+ | 'a'..'z' | 'A'..'Z' | '0'..'9' -> c
4545+ | _ -> '-'
4646+ ) s in
4747+ String.lowercase_ascii mapped
4848+3949let slug_of_fname fname =
4050 let basename = Filename.basename fname in
4151 let no_ext = Filename.chop_extension basename in
4252 match parse_date_prefix no_ext with
4343- | Some (date, slug) -> Ok (slug, Some date)
4444- | None -> Ok (no_ext, None)
5353+ | Some (date, slug) -> Ok (normalize_slug slug, Some date)
5454+ | None -> Ok (normalize_slug no_ext, None)
45554656(** Parse frontmatter using yamlrw's streaming parser.
4757 Uses multi-document support to find the document boundary,