import { visit } from 'unist-util-visit' import { Node } from 'unist' interface LinkNode extends Node { type: 'link' url: string children: Node[] } /** * Detects internal blog post links: * - Relative paths (no http/https) * - Not absolute paths (doesn't start with /) * - Ends with .md */ function isInternalBlogLink(url: string): boolean { return ( !url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('/') && url.includes('.md') ) } /** * Transforms internal .md links to blog routes: * - tech/article.md → /blog/tech/article * - article.md#section → /blog/article#section * - nested/path/post.md?ref=foo → /blog/nested/path/post?ref=foo */ function transformToBlogPath(url: string): string { // Split into path, hash, and query const hashIndex = url.indexOf('#') const queryIndex = url.indexOf('?') let path = url let hash = '' let query = '' if (hashIndex !== -1) { path = url.substring(0, hashIndex) hash = url.substring(hashIndex) } if (queryIndex !== -1 && queryIndex < (hashIndex === -1 ? url.length : hashIndex)) { path = url.substring(0, queryIndex) query = url.substring(queryIndex, hashIndex === -1 ? url.length : hashIndex) } // Remove .md extension const cleanPath = path.replace(/\.md$/, '') // Build final URL return `/blog/${cleanPath}${query}${hash}` } export function remarkInternalLinks() { return (tree: Node) => { visit(tree, 'link', (node: Node) => { const linkNode = node as LinkNode if (isInternalBlogLink(linkNode.url)) { linkNode.url = transformToBlogPath(linkNode.url) } }) } }