Repository navigation
Expand file tree
/
Copy pathgenerate-api-docs.mjs
More file actions
168 lines (137 loc) · 5.65 KB
/
Copy pathgenerate-api-docs.mjs
File metadata and controls
168 lines (137 loc) · 5.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* Generate API reference markdown from the Squad SDK source using TypeDoc.
*
* Usage:
* node scripts/generate-api-docs.mjs
*
* Outputs markdown to docs/src/content/docs/reference/api/.
* Runs TypeDoc, then normalizes filenames for Astro compatibility and
* replaces the generated index with a curated landing page.
*/
import { execFileSync } from 'node:child_process';
import { writeFileSync, readdirSync, readFileSync, renameSync, unlinkSync, mkdirSync, existsSync } from 'node:fs';
import { join, dirname, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..');
const SDK_DIR = join(ROOT, 'packages', 'squad-sdk');
const API_OUT = join(ROOT, 'docs', 'src', 'content', 'docs', 'reference', 'api');
/** Convert a TypeDoc filename to an Astro-safe slug (lowercase, dots → dashes). */
function toSafeFilename(original) {
// Class.RuntimeEventBus.md → class-runtimeeventbus.md
return original.toLowerCase().replace(/\./g, '-').replace(/-md$/, '.md');
}
// Ensure output directory exists (fresh clones may not have it)
mkdirSync(API_OUT, { recursive: true });
// Resolve TypeDoc from either the workspace package or the hoisted root install.
const typedocExecutable = process.platform === 'win32' ? 'typedoc.cmd' : 'typedoc';
const typedocBin = [
join(SDK_DIR, 'node_modules', '.bin', typedocExecutable),
join(ROOT, 'node_modules', '.bin', typedocExecutable),
].find(existsSync);
if (!typedocBin) {
console.error('❌ TypeDoc not found. Run npm install before generating API docs.');
process.exit(1);
}
// Step 1 — Run TypeDoc
console.log('⚙️ Running TypeDoc…');
try {
const typedocCommand = process.platform === 'win32'
? relative(SDK_DIR, typedocBin)
: typedocBin;
execFileSync(typedocCommand, [], {
cwd: SDK_DIR,
stdio: 'inherit',
shell: process.platform === 'win32',
});
} catch (err) {
console.error(`❌ TypeDoc failed. Command: ${typedocBin} (cwd: ${SDK_DIR})`);
console.error(err instanceof Error ? err.message : String(err));
process.exit(1);
}
// Step 2 — Rename files and build rename map
const rawFiles = readdirSync(API_OUT).filter(f => f.endsWith('.md') && f !== 'index.md');
const renameMap = new Map(); // old filename → new filename
for (const oldName of rawFiles) {
const newName = toSafeFilename(oldName);
renameMap.set(oldName, newName);
if (oldName !== newName) {
renameSync(join(API_OUT, oldName), join(API_OUT, newName));
}
}
// Step 3 — Rewrite internal links in all generated files
const renamedFiles = readdirSync(API_OUT).filter(f => f.endsWith('.md') && f !== 'index.md');
for (const file of renamedFiles) {
const filePath = join(API_OUT, file);
let content = readFileSync(filePath, 'utf-8');
let changed = false;
for (const [oldName, newName] of renameMap) {
if (content.includes(oldName)) {
content = content.replaceAll(oldName, newName);
changed = true;
}
}
if (changed) {
writeFileSync(filePath, content);
}
}
// Step 4 — Collect files by category
const categories = {
Classes: [],
Interfaces: [],
Functions: [],
'Type aliases': [],
Variables: [],
};
for (const file of renamedFiles) {
const content = readFileSync(join(API_OUT, file), 'utf-8');
const headingMatch = content.match(/^#\s+\w+:\s+(.+)$/m);
const name = headingMatch ? headingMatch[1] : file.replace(/-/g, '.').replace(/\.md$/, '');
if (file.startsWith('class-')) categories.Classes.push({ name, file });
else if (file.startsWith('interface-')) categories.Interfaces.push({ name, file });
else if (file.startsWith('function-')) categories.Functions.push({ name, file });
else if (file.startsWith('typealias-')) categories['Type aliases'].push({ name, file });
else if (file.startsWith('variable-')) categories.Variables.push({ name, file });
}
for (const cat of Object.values(categories)) {
cat.sort((a, b) => a.name.localeCompare(b.name));
}
// Step 5 — Write curated landing page
const classCount = categories.Classes.length;
const ifaceCount = categories.Interfaces.length;
const fnCount = categories.Functions.length;
const typeCount = categories['Type aliases'].length;
const varCount = categories.Variables.length;
const total = classCount + ifaceCount + fnCount + typeCount + varCount;
let landing = `---
title: SDK API reference
description: Auto-generated TypeScript API reference for @bradygaster/squad-sdk.
---
# SDK API reference
> ⚠️ **Experimental** — Squad is alpha software. APIs may change between releases.
Auto-generated from the \`@bradygaster/squad-sdk\` TypeScript source using [TypeDoc](https://typedoc.org/).
This reference covers **${total}** public exports: ${classCount} classes, ${ifaceCount} interfaces, ${fnCount} functions, ${typeCount} type aliases, and ${varCount} variables.
To regenerate, run:
\`\`\`bash
npm run docs:api
\`\`\`
---
`;
for (const [title, items] of Object.entries(categories)) {
if (items.length === 0) continue;
landing += `## ${title}\n\n`;
for (const { name, file } of items) {
landing += `- [${name}](${file})\n`;
}
landing += '\n';
}
landing += `---
## See also
- [SDK quick reference](/squad/docs/reference/sdk) — curated examples for common SDK usage
- [Tools & hooks](/squad/docs/reference/tools-and-hooks) — custom tools and hook pipeline
- [Config reference](/squad/docs/reference/config) — configuration file options
`;
// Remove the TypeDoc-generated index.md before writing ours
try { unlinkSync(join(API_OUT, 'index.md')); } catch { /* noop */ }
writeFileSync(join(API_OUT, 'index.md'), landing);
console.log(`✅ API reference generated — ${total} exports across ${renamedFiles.length} pages`);