···36 with _ -> None
37 else None
38000000000039let slug_of_fname fname =
40 let basename = Filename.basename fname in
41 let no_ext = Filename.chop_extension basename in
42 match parse_date_prefix no_ext with
43- | Some (date, slug) -> Ok (slug, Some date)
44- | None -> Ok (no_ext, None)
4546(** Parse frontmatter using yamlrw's streaming parser.
47 Uses multi-document support to find the document boundary,
···36 with _ -> None
37 else None
3839+(** Normalize a slug to match Jekyll's slug_of_string behavior:
40+ map all non-alphanumeric characters to hyphens and lowercase. *)
41+let normalize_slug s =
42+ let mapped = String.map (fun c ->
43+ match c with
44+ | 'a'..'z' | 'A'..'Z' | '0'..'9' -> c
45+ | _ -> '-'
46+ ) s in
47+ String.lowercase_ascii mapped
48+49let slug_of_fname fname =
50 let basename = Filename.basename fname in
51 let no_ext = Filename.chop_extension basename in
52 match parse_date_prefix no_ext with
53+ | Some (date, slug) -> Ok (normalize_slug slug, Some date)
54+ | None -> Ok (normalize_slug no_ext, None)
5556(** Parse frontmatter using yamlrw's streaming parser.
57 Uses multi-document support to find the document boundary,