Skip to content

Commit 1e12f01

Browse files
committed
fix(docs): rename TypeDoc slugs Type.Name → Type-Name to fix 404s
Dots in Next.js App Router route segments cause Nextra to drop the parent folder segment. Clicking 'ClientManager' on /api/core would navigate to /api/Class.ClientManager (missing /core) instead of /api/core/Class-ClientManager. Fix: post-process every TypeDoc output folder after generation: 1. Rewrite cross-reference links: Type.Name.md → Type-Name.md 2. Rewrite breadcrumb links: README.md → index.md 3. Rename files: Type.Name.md → Type-Name.md buildMetaForPackage updated to match the new Class-Name slug format.
1 parent 1518735 commit 1e12f01

File tree

1 file changed

+54
-2
lines changed

1 file changed

+54
-2
lines changed

docs-site/scripts/generate-api-docs.mjs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { execSync } from 'child_process';
1515
import {
1616
existsSync,
1717
mkdirSync,
18+
readFileSync,
1819
readdirSync,
1920
renameSync,
2021
writeFileSync,
@@ -101,6 +102,51 @@ const TYPEDOC_BIN = join(DOCS_SITE, 'node_modules', 'typedoc', 'bin', 'typedoc')
101102

102103
// ── Helpers ──────────────────────────────────────────────────────────────────
103104

105+
/**
106+
* TypeDoc --flattenOutputFiles generates names like `Class.ClientManager.md`.
107+
* Dots in Next.js App Router route segments cause Nextra to drop the parent
108+
* folder segment, producing broken links like `/api/Class.ClientManager`
109+
* instead of `/api/core/Class.ClientManager`.
110+
*
111+
* This function:
112+
* 1. Rewrites cross-reference links in all .md files
113+
* `Type.Name.md` → `Type-Name.md`
114+
* `README.md` → `index.md` (TypeDoc breadcrumb links)
115+
* 2. Renames every `Type.Name.md` file → `Type-Name.md`
116+
*/
117+
function fixDotSlugs(pkgOut) {
118+
const TYPE_PREFIXES =
119+
'Class|Interface|TypeAlias|Function|Variable|Enumeration';
120+
const LINK_RE = new RegExp(
121+
`(${TYPE_PREFIXES})\\.([^)"\\s]+\\.md)`,
122+
'g',
123+
);
124+
125+
const files = readdirSync(pkgOut).filter((f) => f.endsWith('.md'));
126+
127+
// Step 1 — rewrite links inside every file before renaming
128+
for (const file of files) {
129+
const path = join(pkgOut, file);
130+
const original = readFileSync(path, 'utf8');
131+
let rewritten = original
132+
// Type.Name.md → Type-Name.md
133+
.replace(LINK_RE, (_, type, rest) => `${type}-${rest}`)
134+
// README.md → index.md (TypeDoc breadcrumb links)
135+
.replace(/\(README\.md\)/g, '(index.md)');
136+
if (rewritten !== original) writeFileSync(path, rewritten);
137+
}
138+
139+
// Step 2 — rename the files themselves
140+
for (const file of files) {
141+
const match = file.match(
142+
new RegExp(`^(${TYPE_PREFIXES})\\.(.+\\.md)$`),
143+
);
144+
if (match) {
145+
renameSync(join(pkgOut, file), join(pkgOut, `${match[1]}-${match[2]}`));
146+
}
147+
}
148+
}
149+
104150
function run(cmd, opts = {}) {
105151
execSync(cmd, { cwd: ROOT, stdio: 'inherit', ...opts });
106152
}
@@ -118,8 +164,9 @@ function buildMetaForPackage(pkgOut) {
118164
const meta = { index: 'Overview' };
119165

120166
for (const { prefix, title } of TYPE_SECTIONS) {
167+
// Files are now named `Class-Foo.md` (hyphen separator, not dot)
121168
const members = files
122-
.filter((f) => f.startsWith(`${prefix}.`))
169+
.filter((f) => f.startsWith(`${prefix}-`))
123170
.sort((a, b) => a.localeCompare(b));
124171

125172
if (members.length === 0) continue;
@@ -130,7 +177,7 @@ function buildMetaForPackage(pkgOut) {
130177
};
131178

132179
for (const slug of members) {
133-
const displayName = slug.replace(/^[^.]+\./, ''); // strip "Class." prefix
180+
const displayName = slug.replace(/^[^-]+-/, ''); // strip "Class-" prefix
134181
meta[slug] = displayName;
135182
}
136183
}
@@ -202,6 +249,11 @@ for (const pkg of PACKAGES) {
202249
renameSync(readme, index);
203250
}
204251

252+
// Rename Type.Name.md → Type-Name.md and rewrite cross-links.
253+
// Dots in Next.js App Router route segments cause Nextra to drop the
254+
// parent folder segment, producing /api/Class.Foo instead of /api/core/Class.Foo.
255+
fixDotSlugs(pkgOut);
256+
205257
writeMetaJs(pkgOut, buildMetaForPackage(pkgOut));
206258

207259
apiMeta[pkg.slug] = pkg.name;

0 commit comments

Comments
 (0)