Summary
toMarkdownPath checks endsWith(".md") before it strips trailing slashes, so a markdown path that arrives with a trailing slash gets a doubled .md extension.
Where
packages/core/src/paths.ts:20-24
export function toMarkdownPath(pathname: string): string {
if (pathname.endsWith(".md")) return pathname; // "/blog/post.md/" fails this
const trimmed = pathname.replace(/\/+$/, ""); // -> "/blog/post.md"
if (trimmed === "") return "/index.md";
return trimmed + ".md"; // -> "/blog/post.md.md"
}
Reproduction
import { toMarkdownPath, toMarkdownUrl } from "@dualmark/core";
toMarkdownPath("/blog/post.md/"); // "/blog/post.md.md" (should be "/blog/post.md")
toMarkdownUrl("https://x.com/a.md/"); // "https://x.com/a.md.md"
A request for a markdown twin with a trailing slash resolves to a nonexistent …/post.md.md and 404s. The function's own JSDoc claims idempotency on already-.md paths, and content-negotiation.md section 6 defines trailing-slash stripping for the canonical mapping.
Proposed fix
Strip trailing slashes before the .md check so an already-.md path (with or without a trailing slash) maps back to itself. PR incoming.
Summary
toMarkdownPathchecksendsWith(".md")before it strips trailing slashes, so a markdown path that arrives with a trailing slash gets a doubled.mdextension.Where
packages/core/src/paths.ts:20-24Reproduction
A request for a markdown twin with a trailing slash resolves to a nonexistent
…/post.md.mdand 404s. The function's own JSDoc claims idempotency on already-.mdpaths, and content-negotiation.md section 6 defines trailing-slash stripping for the canonical mapping.Proposed fix
Strip trailing slashes before the
.mdcheck so an already-.mdpath (with or without a trailing slash) maps back to itself. PR incoming.