| 258 | |
| 259 | // Build map: filename basename (lowercase) → permalink, from all locale .md files |
| 260 | function buildBasenameToPermalink(dir: string): Map<string, string> { |
| 261 | const map = new Map<string, string>(); |
| 262 | function walk(current: string) { |
| 263 | for (const entry of fs.readdirSync(current, { withFileTypes: true })) { |
| 264 | const full = path.join(current, entry.name); |
| 265 | if (entry.isDirectory()) walk(full); |
| 266 | else if (entry.isFile() && entry.name.endsWith(".md")) { |
| 267 | const raw = fs.readFileSync(full, "utf8"); |
| 268 | const parsed = matter(raw); |
| 269 | const permalink = parsed.data?.permalink as string | undefined; |
| 270 | if (!permalink) continue; |
| 271 | const basename = entry.name.replace(/\.md$/, "").toLowerCase(); |
| 272 | map.set(basename, permalink); |
| 273 | // Also register aliases |
| 274 | const aliases = parsed.data?.aliases as string[] | undefined; |
| 275 | for (const alias of aliases ?? []) { |
| 276 | const aliasBase = path.basename(alias).toLowerCase(); |
| 277 | if (!map.has(aliasBase)) map.set(aliasBase, permalink); |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | } |
| 282 | walk(dir); |
| 283 | return map; |
| 284 | } |
| 285 | |
| 286 | function fixHeadingLinks(content: string, headingsMap: HeadingsMap, basenameToPermalink: Map<string, string>): string { |
| 287 | // Match [[Target#Heading]] or [[Target#Heading|Display]] or ![[Target#Heading]] |