export declare function initDocsContext(opts: { repoRoot: string; submodules: readonly string[]; extraExcludedPrefixes?: readonly string[]; }): void; export type LinkSeverity = "error" | "warning"; export type LinkRule = "missing-target" | "missing-fragment" | "case-mismatch" | "escapes-repo"; export interface LinkFinding { severity: LinkSeverity; repo: string; /** Source file, relative to its repo root. */ path: string; line: number; rule: LinkRule; /** The raw link target as written, fragment included. */ target: string; message: string; /** Populated for case-mismatch: the path that does exist on disk. */ suggestion?: string; } export interface LinkOpts { /** Limit to one submodule, or "." for the parent repo. */ repo?: string; /** Skip heading-fragment validation; check target existence only. */ noFragments?: boolean; /** Report findings in immutable-history docs at error severity too. */ strict?: boolean; /** Also flag links that resolve outside their own repo root. */ checkEscapes?: boolean; } export interface LinkReport { repo: string | null; files_scanned: number; links_checked: number; links_skipped: number; error_count: number; warning_count: number; findings: LinkFinding[]; } /** * Blank out fenced code blocks, preserving line count so reported line numbers * stay accurate. Replacing rather than deleting keeps the line index trivially * correct. */ export declare function maskFences(content: string): string; /** * Blank fenced blocks and inline code spans, preserving line count and column * positions. This is the input for link extraction only. * * Anchor collection deliberately uses {@link maskFences} instead: a heading is * very often entirely inline code (`### \`some command\``), and masking the span * would erase the heading text and with it the anchor the document really has. */ export declare function maskCode(content: string): string; export interface ExtractedLink { target: string; line: number; } /** * Pull every link target out of masked Markdown: inline links, images, and * reference definitions. * * The scan keys off each `](` rather than trying to match the link text, which * is what makes nested constructs work: in `[![alt](img.png)](page.md)` both * destinations are found, where a text-matching regex sees only one. Angle- * bracket-wrapped destinations are unwrapped here so a legitimately spaced * filename is not later mistaken for placeholder syntax. */ export declare function extractLinks(masked: string): ExtractedLink[]; /** * GitHub's heading-anchor slug: lowercase, drop punctuation other than hyphen * and underscore, then map each remaining space to one hyphen. Duplicate slugs * in one document get -1, -2, ... suffixes in document order. * * Runs of whitespace are deliberately NOT collapsed. Dropping a punctuation * mark leaves the spaces that surrounded it, so "Intent — capture" becomes * "intent--capture" with a double hyphen, and that is the anchor GitHub renders * and the one real links in the wild are written against. */ export declare function slugify(heading: string): string; /** * Every fragment a reader can legitimately target in one document: Markdown * heading slugs (ATX and Setext), explicit `{#custom-id}` suffixes, and HTML * `id=` / `name=` attributes, which docs use for stable anchors that survive a * heading rename. */ export declare function collectAnchors(content: string): Set; interface CheckFileOpts { repoName: string; repoPath: string; /** Source file path relative to repoPath. */ rel: string; content: string; noFragments: boolean; strict: boolean; checkEscapes: boolean; /** Cache of absolute file path -> anchor set, shared across the run. */ anchorCache: Map>; } interface FileResult { findings: LinkFinding[]; checked: number; skipped: number; } export declare function checkFile(opts: CheckFileOpts): FileResult; export declare function runLinks(opts: LinkOpts): Promise; export {}; //# sourceMappingURL=docs-links.d.ts.map