56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
/**
|
|
* Link completion source — extracts markdown headings from all .md files.
|
|
*
|
|
* Produces two entries per heading section, plus one for the file itself.
|
|
* Uses github-slugger to match marked-gfm-heading-id's generated IDs.
|
|
*/
|
|
|
|
import Slugger from "github-slugger";
|
|
import type { CompletionSource, LinkCompletion } from "../types.js";
|
|
|
|
export const linksSource: CompletionSource = {
|
|
key: "links",
|
|
|
|
scan(index) {
|
|
const items: LinkCompletion[] = [];
|
|
|
|
for (const [filePath, content] of Object.entries(index)) {
|
|
if (!filePath.endsWith(".md")) continue;
|
|
|
|
// Strip .md extension for the router-friendly path
|
|
const basePath = filePath.replace(/\.md$/, "");
|
|
const fileName = fileNameFromPath(basePath);
|
|
const slugger = new Slugger();
|
|
|
|
// Add the file itself as a link (whole article)
|
|
items.push({
|
|
path: basePath,
|
|
label: fileName,
|
|
section: null,
|
|
});
|
|
|
|
// Parse headings for section-scoped links
|
|
const headingRegex = /^(#{1,6})\s+(.+)$/gm;
|
|
let match: RegExpExecArray | null;
|
|
|
|
while ((match = headingRegex.exec(content)) !== null) {
|
|
const title = match[2].trim();
|
|
const id = slugger.slug(title.toLowerCase());
|
|
|
|
items.push({
|
|
path: basePath,
|
|
label: `${fileName} § ${title}`,
|
|
section: id,
|
|
});
|
|
}
|
|
}
|
|
|
|
return items;
|
|
},
|
|
};
|
|
|
|
function fileNameFromPath(path: string): string {
|
|
const parts = path.split("/").filter(Boolean);
|
|
return parts[parts.length - 1] || path;
|
|
}
|