docs: fix custom-site internal link routing

This commit is contained in:
callumalpass 2026-02-21 13:57:07 +11:00
parent 735706ead4
commit 3e40202a09
2 changed files with 63 additions and 3 deletions

View file

@ -62,6 +62,24 @@ function parseMarkdown(raw) {
return { fm, html };
}
function splitHref(href) {
const queryIndex = href.indexOf('?');
const hashIndex = href.indexOf('#');
let end = href.length;
if (queryIndex !== -1 && queryIndex < end) end = queryIndex;
if (hashIndex !== -1 && hashIndex < end) end = hashIndex;
return { pathPart: href.slice(0, end), suffix: href.slice(end) };
}
function mdLinkPathToUrl(linkPath, mdPath) {
const pageDir = '/' + path.posix.dirname(mdPath).replace(/^\.(?:\/|$)/, '');
const resolved = linkPath.startsWith('/')
? path.posix.normalize(linkPath)
: path.posix.normalize(path.posix.join(pageDir || '/', linkPath));
const relativeMdPath = resolved.replace(/^\/+/, '');
return mdPathToUrl(relativeMdPath);
}
// Rewrite relative image src/href paths to absolute URL paths.
// Markdown files reference assets relative to their own location, but the
// built HTML is served from a URL path that doesn't match the source path
@ -76,6 +94,24 @@ function resolveAssetPaths(html, mdPath) {
});
}
// Rewrite local markdown links to route URLs so links stay valid regardless
// of the current page path (e.g. `foo.md` -> `/foo/`).
function resolveMarkdownLinks(html, mdPath) {
return html.replace(/(<a\b[^>]*\shref=")([^"]+)(")/g, (_, pre, href, post) => {
if (!href) return _;
if (href.startsWith('#')) return _;
if (href.startsWith('//')) return _;
if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(href)) return _;
const { pathPart, suffix } = splitHref(href);
if (!pathPart) return _;
if (!/\.md$/i.test(pathPart)) return _;
const rewritten = mdLinkPathToUrl(pathPart, mdPath);
return pre + rewritten + suffix + post;
});
}
function extractTitle(html, fm) {
if (fm.title) return fm.title;
const m = html.match(/<h1[^>]*>([\s\S]*?)<\/h1>/);
@ -158,6 +194,26 @@ async function ensureDir(p) {
await fs.mkdir(p, { recursive: true });
}
async function listMarkdownFiles(rootDir) {
const files = [];
async function walk(dir) {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
await walk(fullPath);
continue;
}
if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
files.push(path.relative(rootDir, fullPath).split(path.sep).join('/'));
}
}
await walk(rootDir);
return files;
}
async function readSource(mdPath) {
// Local override takes precedence over the source docs file
const override = path.join(SRC, 'overrides', mdPath);
@ -204,8 +260,10 @@ async function main() {
await fs.copyFile(cname, path.join(DIST, 'CNAME'));
}
// Build every page listed in the nav
const pages = flattenNav(nav);
// Build every markdown doc so cross-links outside the nav still resolve
const navPages = flattenNav(nav);
const allDocPages = await listMarkdownFiles(DOCS);
const pages = [...new Set([...navPages, ...allDocPages])];
let built = 0, skipped = 0;
for (const mdPath of pages) {
@ -213,7 +271,7 @@ async function main() {
if (!raw) { skipped++; continue; }
const { fm, html: rawHtml } = parseMarkdown(raw);
const html = resolveAssetPaths(rawHtml, mdPath);
const html = resolveMarkdownLinks(resolveAssetPaths(rawHtml, mdPath), mdPath);
const url = mdPathToUrl(mdPath);
const title = extractTitle(html, fm);
const body = stripH1(html);

View file

@ -35,3 +35,5 @@ Example:
- Thanks to @martin-forge for reporting
- Consolidated documentation cleanup for accuracy and clarity across API, webhook, NLP, privacy, settings, and view docs (corrected outdated endpoint/behavior details, normalized current settings paths, and tightened non-release prose)
- Fixed a broken docs cross-reference from Property Types Reference to Task Properties settings
- Fixed docs site link generation so internal Markdown links resolve to route URLs instead of broken `.md` paths (for example `/views/default-base-templates/`)
- Fixed docs release-note links by building all Markdown docs pages, including pages not listed directly in sidebar nav