@abstractdata/create-docs 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/bin/.fuse_hidden0000000a00000002 +194 -0
- package/bin/.fuse_hidden0000000d00000003 +194 -0
- package/bin/cli.js +194 -0
- package/package.json +38 -0
- package/template/.claude/skills/abstract-data-docs-author/SKILL.md +305 -0
- package/template/.claude/skills/abstract-data-setup/SKILL.md +555 -0
- package/template/.cursor/rules/abstract-data-docs-author.mdc +311 -0
- package/template/.cursor/rules/abstract-data-setup.mdc +561 -0
- package/template/.cursor/rules/welcome.mdc +29 -0
- package/template/.fuse_hidden0000001e00000008 +75 -0
- package/template/.fuse_hidden0000002100000009 +46 -0
- package/template/.fuse_hidden000000210000000a +75 -0
- package/template/.fuse_hidden000000240000000b +46 -0
- package/template/.github/copilot-instructions.md +893 -0
- package/template/.github/workflows/deploy-cloudflare.yml +50 -0
- package/template/.github/workflows/deploy-vercel.yml +47 -0
- package/template/.github/workflows/deploy.yml +55 -0
- package/template/CLAUDE.md +46 -0
- package/template/README.md +75 -0
- package/template/astro.config.mjs +69 -0
- package/template/package.json +25 -0
- package/template/public/favicon.svg +26 -0
- package/template/scripts/build-python-docs.mjs +385 -0
- package/template/scripts/build-ts-docs.mjs +349 -0
- package/template/scripts/python-autodoc.json +10 -0
- package/template/scripts/ts-autodoc.json +10 -0
- package/template/src/assets/README.md +1 -0
- package/template/src/content/docs/index.mdx +81 -0
- package/template/src/content/docs/quickstart.md +162 -0
- package/template/src/content.config.ts +46 -0
- package/template/src/env.d.ts +2 -0
- package/template/tsconfig.json +5 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* build-ts-docs.mjs
|
|
4
|
+
*
|
|
5
|
+
* Generates Starlight-compatible Markdown API reference from a
|
|
6
|
+
* TypeScript project's source via TypeDoc + typedoc-plugin-markdown.
|
|
7
|
+
*
|
|
8
|
+
* Reads `scripts/ts-autodoc.json` for configuration:
|
|
9
|
+
* - entryPoints: array of TS entry files (e.g. ["../../my-lib/src/index.ts"])
|
|
10
|
+
* - tsconfig: path to the project's tsconfig.json
|
|
11
|
+
* - outputDir: where the generated .md pages land
|
|
12
|
+
* - githubPages: pass through to TypeDoc
|
|
13
|
+
* - skipErrorChecking: pass through to TypeDoc
|
|
14
|
+
* - repoUrl: (optional) base URL for "View on GitHub" footer
|
|
15
|
+
* - repoBranch: (optional) default 'main'
|
|
16
|
+
* - versions: (optional) array of { tag, label, default } for versioned docs.
|
|
17
|
+
* When present, each tag triggers an independent build
|
|
18
|
+
* from a `git worktree` checkout into <outputDir>/<safeTag>/.
|
|
19
|
+
* The default version is also aliased at the un-versioned
|
|
20
|
+
* URL so existing links keep resolving.
|
|
21
|
+
*
|
|
22
|
+
* For each module, TypeDoc emits a markdown file. The orchestrator
|
|
23
|
+
* post-processes each one to add Starlight `title:` frontmatter, a `version:`
|
|
24
|
+
* field when versioned, and a stale-version banner on non-default builds.
|
|
25
|
+
*
|
|
26
|
+
* Run:
|
|
27
|
+
* bun run docs:ts
|
|
28
|
+
*
|
|
29
|
+
* Requires:
|
|
30
|
+
* bun add -d typedoc typedoc-plugin-markdown
|
|
31
|
+
*/
|
|
32
|
+
import { execSync } from 'node:child_process';
|
|
33
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
34
|
+
import { dirname, join, resolve, relative } from 'node:path';
|
|
35
|
+
import { fileURLToPath } from 'node:url';
|
|
36
|
+
import { tmpdir } from 'node:os';
|
|
37
|
+
|
|
38
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
const PROJECT_ROOT = resolve(__dirname, '..');
|
|
40
|
+
const CONFIG_PATH = resolve(__dirname, 'ts-autodoc.json');
|
|
41
|
+
|
|
42
|
+
const c = {
|
|
43
|
+
reset: '\x1b[0m', dim: '\x1b[2m', cyan: '\x1b[36m', gold: '\x1b[33m',
|
|
44
|
+
red: '\x1b[31m', green: '\x1b[32m',
|
|
45
|
+
};
|
|
46
|
+
const log = (...a) => console.log(...a);
|
|
47
|
+
const die = (msg) => { console.error(`${c.red}error${c.reset} ${msg}`); process.exit(1); };
|
|
48
|
+
|
|
49
|
+
// ─── Load config ──────────────────────────────────────────────────────
|
|
50
|
+
if (!existsSync(CONFIG_PATH)) die(`Missing config: ${CONFIG_PATH}`);
|
|
51
|
+
const cfg = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
|
|
52
|
+
if (!Array.isArray(cfg.entryPoints) || cfg.entryPoints.length === 0) {
|
|
53
|
+
die('ts-autodoc.json: `entryPoints` must be a non-empty array.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const ROOT_OUTPUT = resolve(PROJECT_ROOT, cfg.outputDir ?? 'src/content/docs/api/ts');
|
|
57
|
+
const ROOT_TSCONFIG = cfg.tsconfig ? resolve(PROJECT_ROOT, cfg.tsconfig) : null;
|
|
58
|
+
|
|
59
|
+
// ─── Verify TypeDoc available ─────────────────────────────────────────
|
|
60
|
+
log(`${c.dim}→ checking typedoc${c.reset}`);
|
|
61
|
+
try {
|
|
62
|
+
execSync('npx --no-install typedoc --version', { cwd: PROJECT_ROOT, stdio: 'ignore' });
|
|
63
|
+
} catch {
|
|
64
|
+
die(`typedoc not found. Install it as a dev dep:
|
|
65
|
+
bun add -d typedoc typedoc-plugin-markdown
|
|
66
|
+
Then re-run this script.`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── Versioning setup ────────────────────────────────────────────────
|
|
70
|
+
function findGitRoot(start) {
|
|
71
|
+
let dir = start;
|
|
72
|
+
while (true) {
|
|
73
|
+
if (existsSync(join(dir, '.git'))) return dir;
|
|
74
|
+
const parent = dirname(dir);
|
|
75
|
+
if (parent === dir) return null;
|
|
76
|
+
dir = parent;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const versions = Array.isArray(cfg.versions) ? cfg.versions : null;
|
|
81
|
+
let SOURCE_REPO_ROOT = null;
|
|
82
|
+
let ENTRY_POINTS_REL = null;
|
|
83
|
+
let TSCONFIG_REL = null;
|
|
84
|
+
if (versions) {
|
|
85
|
+
if (versions.length === 0) die('`versions` is an empty array — set it to null/omit, or list at least one version.');
|
|
86
|
+
if (!versions.some((v) => v.tag)) die('`versions[].tag` is required on every entry.');
|
|
87
|
+
if (versions.filter((v) => v.default).length > 1) die('Only one `versions[].default: true` allowed.');
|
|
88
|
+
if (!versions.some((v) => v.default)) {
|
|
89
|
+
log(`${c.gold}warn${c.reset} no version marked default; treating the first one (${versions[0].tag}) as default`);
|
|
90
|
+
versions[0].default = true;
|
|
91
|
+
}
|
|
92
|
+
// Use the first entry point to find the source git repo. All entries
|
|
93
|
+
// must live under the same repo for versioned builds to make sense.
|
|
94
|
+
const firstEntry = resolve(PROJECT_ROOT, cfg.entryPoints[0]);
|
|
95
|
+
SOURCE_REPO_ROOT = findGitRoot(firstEntry);
|
|
96
|
+
if (!SOURCE_REPO_ROOT) {
|
|
97
|
+
die(`versions[] is configured but no .git directory was found above ${firstEntry}.\n Versioned builds require the source to be a git checkout.`);
|
|
98
|
+
}
|
|
99
|
+
ENTRY_POINTS_REL = cfg.entryPoints.map((e) =>
|
|
100
|
+
relative(SOURCE_REPO_ROOT, resolve(PROJECT_ROOT, e)),
|
|
101
|
+
);
|
|
102
|
+
TSCONFIG_REL = ROOT_TSCONFIG ? relative(SOURCE_REPO_ROOT, ROOT_TSCONFIG) : null;
|
|
103
|
+
log(`${c.dim}→ source repo: ${SOURCE_REPO_ROOT}${c.reset}`);
|
|
104
|
+
log(`${c.dim}→ entryPoints (rel): ${ENTRY_POINTS_REL.join(', ')}${c.reset}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Make a tag filesystem-safe for use as a directory name. We have to be
|
|
108
|
+
// strict here: Astro's slug normalizer strips dots from URL segments
|
|
109
|
+
// (`0.1.0` → `010`), so if our directory names contain dots the URL the
|
|
110
|
+
// VersionPicker constructs won't match the rendered URL. Convert dots
|
|
111
|
+
// to dashes (and any other non-alphanumeric to dashes).
|
|
112
|
+
// v0.3.0 → 0-3-0
|
|
113
|
+
// v1.0.0-rc.1 → 1-0-0-rc-1
|
|
114
|
+
function safeTag(tag) {
|
|
115
|
+
return tag.replace(/^v/, '').replace(/[^a-zA-Z0-9_-]/g, '-');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── Per-build pipeline ──────────────────────────────────────────────
|
|
119
|
+
function buildOnce({ entryPoints, tsconfig, version }) {
|
|
120
|
+
const outDir = version ? join(ROOT_OUTPUT, safeTag(version.tag)) : ROOT_OUTPUT;
|
|
121
|
+
mkdirSync(outDir, { recursive: true });
|
|
122
|
+
|
|
123
|
+
const tagPrefix = version ? `${c.cyan}[${version.label ?? version.tag}]${c.reset} ` : '';
|
|
124
|
+
log(`${c.dim}→ ${tagPrefix}generating TypeScript API pages${c.reset}`);
|
|
125
|
+
|
|
126
|
+
const args = [
|
|
127
|
+
'--plugin', 'typedoc-plugin-markdown',
|
|
128
|
+
'--out', outDir,
|
|
129
|
+
'--readme', 'none',
|
|
130
|
+
'--hideBreadcrumbs', 'true',
|
|
131
|
+
'--hidePageHeader', 'true',
|
|
132
|
+
];
|
|
133
|
+
if (tsconfig) args.push('--tsconfig', tsconfig);
|
|
134
|
+
if (cfg.skipErrorChecking) args.push('--skipErrorChecking');
|
|
135
|
+
for (const entry of entryPoints) args.push(entry);
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
execSync(`npx typedoc ${args.map((a) => `"${a}"`).join(' ')}`,
|
|
139
|
+
{ cwd: PROJECT_ROOT, stdio: 'inherit' });
|
|
140
|
+
} catch {
|
|
141
|
+
log(`${c.red}error${c.reset} ${tagPrefix}typedoc failed; skipping this build`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Post-process every emitted .md
|
|
146
|
+
log(`${c.dim}→ ${tagPrefix}adding Starlight frontmatter${c.reset}`);
|
|
147
|
+
|
|
148
|
+
function walk(dir) {
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
151
|
+
const full = join(dir, entry.name);
|
|
152
|
+
if (entry.isDirectory()) out.push(...walk(full));
|
|
153
|
+
else if (entry.name.endsWith('.md')) out.push(full);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const pages = [];
|
|
159
|
+
for (const file of walk(outDir)) {
|
|
160
|
+
let content = readFileSync(file, 'utf8');
|
|
161
|
+
if (content.startsWith('---\n')) continue;
|
|
162
|
+
|
|
163
|
+
const h1 = content.match(/^# (.+?)$/m);
|
|
164
|
+
const fallbackTitle = relative(outDir, file).replace(/\.md$/, '').replace(/[\\/_]/g, ' ');
|
|
165
|
+
const title = (h1?.[1] ?? fallbackTitle).trim().replace(/\\_/g, '_');
|
|
166
|
+
const body = h1 ? content.replace(h1[0] + '\n', '') : content;
|
|
167
|
+
|
|
168
|
+
const desc = body.split('\n').find((l) => {
|
|
169
|
+
const t = l.trim();
|
|
170
|
+
if (!t) return false;
|
|
171
|
+
if (/^#{1,6} /.test(t)) return false;
|
|
172
|
+
if (t.startsWith('```')) return false;
|
|
173
|
+
if (t.startsWith('|')) return false;
|
|
174
|
+
if (/^[-*+] /.test(t)) return false;
|
|
175
|
+
if (/^<[^>]+>/.test(t)) return false;
|
|
176
|
+
return true;
|
|
177
|
+
});
|
|
178
|
+
const description = (desc ?? `API reference for \`${title}\`.`)
|
|
179
|
+
.trim().replace(/`/g, '').replace(/"/g, "'").slice(0, 160);
|
|
180
|
+
|
|
181
|
+
const fmLines = ['---', `title: ${title}`, `description: "${description}"`];
|
|
182
|
+
if (version) {
|
|
183
|
+
// Emit version metadata. `versionDefault: true` lets the bundled
|
|
184
|
+
// <VersionPicker> auto-discover which version to pre-select without
|
|
185
|
+
// duplicating the canonical list outside the autodoc JSON config.
|
|
186
|
+
fmLines.push(`version: "${version.tag}"`);
|
|
187
|
+
if (version.label) fmLines.push(`versionLabel: "${version.label}"`);
|
|
188
|
+
if (version.default) fmLines.push(`versionDefault: true`);
|
|
189
|
+
}
|
|
190
|
+
fmLines.push('---', '');
|
|
191
|
+
const frontmatter = fmLines.join('\n');
|
|
192
|
+
pages.push({ file, title, description, body, frontmatter });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Thin-page post-processor (TS)
|
|
196
|
+
let bannered = 0, enriched = 0;
|
|
197
|
+
const landingPage = pages.find((p) => /^(index|readme|globals|modules)\.md$/i.test(relative(outDir, p.file)));
|
|
198
|
+
|
|
199
|
+
for (const page of pages) {
|
|
200
|
+
const proseLines = page.body.split('\n').filter((l) => {
|
|
201
|
+
const t = l.trim();
|
|
202
|
+
if (!t) return false;
|
|
203
|
+
if (/^#{1,6} /.test(t)) return false;
|
|
204
|
+
if (t.startsWith('```')) return false;
|
|
205
|
+
if (t.startsWith('|') || /^[-=]{3,}/.test(t)) return false;
|
|
206
|
+
if (/^[-*+] /.test(t)) return false;
|
|
207
|
+
if (/^<[^>]+>/.test(t)) return false;
|
|
208
|
+
return true;
|
|
209
|
+
}).length;
|
|
210
|
+
const bodyChars = page.body.replace(/\s+/g, '').length;
|
|
211
|
+
const isThin = bodyChars < 150 && proseLines < 1;
|
|
212
|
+
|
|
213
|
+
let newBody = page.body;
|
|
214
|
+
let touched = false;
|
|
215
|
+
|
|
216
|
+
if (version && !version.default) {
|
|
217
|
+
const defaultV = (cfg.versions ?? []).find((v) => v.default);
|
|
218
|
+
const latestLabel = defaultV ? (defaultV.label ?? defaultV.tag) : 'latest';
|
|
219
|
+
const apiBase = `/${cfg.outputDir.replace(/^src\/content\/docs\/?/, '').replace(/\/$/, '')}`;
|
|
220
|
+
const sameRel = relative(outDir, page.file).replace(/\.md$/, '');
|
|
221
|
+
const latestPath = defaultV
|
|
222
|
+
? `${apiBase}/${safeTag(defaultV.tag)}/${sameRel}/`
|
|
223
|
+
: null;
|
|
224
|
+
const link = latestPath ? `[${latestLabel} →](${latestPath})` : latestLabel;
|
|
225
|
+
const stale = [
|
|
226
|
+
'', `:::caution[Older version]`,
|
|
227
|
+
`You're viewing **${version.label ?? version.tag}**. Latest is ${link}.`,
|
|
228
|
+
':::', '',
|
|
229
|
+
].join('\n');
|
|
230
|
+
newBody = stale + newBody;
|
|
231
|
+
touched = true;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (page === landingPage) {
|
|
235
|
+
const others = pages.filter((p) => p !== landingPage);
|
|
236
|
+
if (others.length > 0) {
|
|
237
|
+
const lines = ['', '## Submodules', ''];
|
|
238
|
+
for (const sib of others) {
|
|
239
|
+
const rel = relative(outDir, sib.file).replace(/\.md$/, '');
|
|
240
|
+
const summary = sib.description && !sib.description.startsWith('API reference for')
|
|
241
|
+
? ` — ${sib.description}` : '';
|
|
242
|
+
lines.push(`- [\`${sib.title}\`](./${rel}.md)${summary}`);
|
|
243
|
+
}
|
|
244
|
+
lines.push('');
|
|
245
|
+
const submodulesSection = lines.join('\n');
|
|
246
|
+
if (isThin) {
|
|
247
|
+
newBody = newBody + `\nTop-level entry — see modules below for the full API surface.\n${submodulesSection}`;
|
|
248
|
+
} else {
|
|
249
|
+
newBody = newBody.replace(/\s+$/, '') + '\n' + submodulesSection;
|
|
250
|
+
}
|
|
251
|
+
enriched += 1;
|
|
252
|
+
touched = true;
|
|
253
|
+
log(`${c.green} ✓${c.reset} ${tagPrefix}added Submodules to ${relative(outDir, page.file)}`);
|
|
254
|
+
}
|
|
255
|
+
} else if (isThin) {
|
|
256
|
+
const noteBlock = [
|
|
257
|
+
'', ':::note[This page is sparse]',
|
|
258
|
+
`The auto-generated reference for \`${page.title}\` is short. Expanding the leading \`/** ... */\` TSDoc comment in the source (purpose, when to use it, a tiny example) would populate this page with real context.`,
|
|
259
|
+
':::', '',
|
|
260
|
+
].join('\n');
|
|
261
|
+
newBody = noteBlock + newBody;
|
|
262
|
+
bannered += 1;
|
|
263
|
+
touched = true;
|
|
264
|
+
log(`${c.gold} ⚠${c.reset} ${tagPrefix}thin-page banner on ${relative(outDir, page.file)}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (touched && cfg.repoUrl) {
|
|
268
|
+
const branch = version ? version.tag : (cfg.repoBranch ?? 'main');
|
|
269
|
+
const repo = cfg.repoUrl.replace(/\/$/, '');
|
|
270
|
+
newBody = newBody.replace(/\s+$/, '') +
|
|
271
|
+
`\n\n## See also\n\n- [View on GitHub](${repo}/tree/${branch})\n`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
writeFileSync(page.file, page.frontmatter + newBody);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
log('');
|
|
278
|
+
log(`${c.green}✓${c.reset} ${tagPrefix}Generated ${c.gold}${pages.length}${c.reset} TS API page${pages.length === 1 ? '' : 's'} in ${c.cyan}${relative(PROJECT_ROOT, outDir)}${c.reset}/`);
|
|
279
|
+
if (enriched || bannered) log(`${c.dim} ${enriched} landing page${enriched === 1 ? '' : 's'} enriched, ${bannered} thin page${bannered === 1 ? '' : 's'} flagged${c.reset}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ─── Run: single-build or per-version with worktrees ──────────────────
|
|
283
|
+
const createdWorktrees = [];
|
|
284
|
+
|
|
285
|
+
function cleanup() {
|
|
286
|
+
for (const wt of createdWorktrees) {
|
|
287
|
+
try {
|
|
288
|
+
execSync(`git -C "${SOURCE_REPO_ROOT}" worktree remove --force "${wt}"`, { stdio: 'ignore' });
|
|
289
|
+
} catch {
|
|
290
|
+
try { rmSync(wt, { recursive: true, force: true }); } catch {}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
process.on('exit', cleanup);
|
|
295
|
+
process.on('SIGINT', () => { cleanup(); process.exit(130); });
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
if (!versions) {
|
|
299
|
+
buildOnce({
|
|
300
|
+
entryPoints: cfg.entryPoints.map((e) => resolve(PROJECT_ROOT, e)),
|
|
301
|
+
tsconfig: ROOT_TSCONFIG,
|
|
302
|
+
version: null,
|
|
303
|
+
});
|
|
304
|
+
} else {
|
|
305
|
+
for (const v of versions) {
|
|
306
|
+
const wt = mkdtempSync(join(tmpdir(), `tsdoc-${safeTag(v.tag)}-`));
|
|
307
|
+
createdWorktrees.push(wt);
|
|
308
|
+
log(`${c.dim}→ git worktree add ${wt} ${v.tag}${c.reset}`);
|
|
309
|
+
try {
|
|
310
|
+
execSync(`git -C "${SOURCE_REPO_ROOT}" worktree add --detach "${wt}" "${v.tag}"`, { stdio: 'inherit' });
|
|
311
|
+
} catch {
|
|
312
|
+
log(`${c.red}error${c.reset} failed to create worktree for ${v.tag}; skipping`);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
const wtEntries = ENTRY_POINTS_REL.map((rel) => join(wt, rel));
|
|
316
|
+
const wtTsconfig = TSCONFIG_REL ? join(wt, TSCONFIG_REL) : null;
|
|
317
|
+
const missing = wtEntries.find((p) => !existsSync(p));
|
|
318
|
+
if (missing) {
|
|
319
|
+
log(`${c.gold}warn${c.reset} ${v.tag}: entry point ${missing} not found; skipping`);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
buildOnce({ entryPoints: wtEntries, tsconfig: wtTsconfig, version: v });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Alias the default version at un-versioned URLs
|
|
326
|
+
const defaultV = versions.find((v) => v.default);
|
|
327
|
+
if (defaultV) {
|
|
328
|
+
log(`${c.dim}→ aliasing ${defaultV.tag} as the default (un-versioned) build${c.reset}`);
|
|
329
|
+
const wt = mkdtempSync(join(tmpdir(), `tsdoc-default-`));
|
|
330
|
+
createdWorktrees.push(wt);
|
|
331
|
+
try {
|
|
332
|
+
execSync(`git -C "${SOURCE_REPO_ROOT}" worktree add --detach "${wt}" "${defaultV.tag}"`, { stdio: 'ignore' });
|
|
333
|
+
const wtEntries = ENTRY_POINTS_REL.map((rel) => join(wt, rel));
|
|
334
|
+
const wtTsconfig = TSCONFIG_REL ? join(wt, TSCONFIG_REL) : null;
|
|
335
|
+
buildOnce({ entryPoints: wtEntries, tsconfig: wtTsconfig, version: null });
|
|
336
|
+
} catch (err) {
|
|
337
|
+
log(`${c.gold}warn${c.reset} default-alias build failed: ${err.message}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} finally {
|
|
342
|
+
cleanup();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
log('');
|
|
346
|
+
log(`${c.dim}Sidebar wiring (astro.config.mjs):${c.reset}`);
|
|
347
|
+
log(`${c.dim} { label: 'TS API', autogenerate: { directory: '${cfg.outputDir.replace(/^src\/content\/docs\/?/, '')}' } }${c.reset}`);
|
|
348
|
+
if (versions) log(`${c.dim} → with ${versions.length} version${versions.length === 1 ? '' : 's'}, the sidebar auto-groups by version subdirectory.${c.reset}`);
|
|
349
|
+
log('');
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "Configuration for scripts/build-python-docs.mjs. Adjust to point at your Python source.",
|
|
3
|
+
"searchPath": "../../python-example/src",
|
|
4
|
+
"modules": [
|
|
5
|
+
"example_module"
|
|
6
|
+
],
|
|
7
|
+
"outputDir": "src/content/docs/api",
|
|
8
|
+
"repoUrl": "",
|
|
9
|
+
"repoBranch": "main"
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "Configuration for scripts/build-ts-docs.mjs. Adjust to point at your TypeScript project.",
|
|
3
|
+
"entryPoints": ["../../packages/your-lib/src/index.ts"],
|
|
4
|
+
"tsconfig": "../../packages/your-lib/tsconfig.json",
|
|
5
|
+
"outputDir": "src/content/docs/api/ts",
|
|
6
|
+
"githubPages": false,
|
|
7
|
+
"skipErrorChecking": true,
|
|
8
|
+
"repoUrl": "",
|
|
9
|
+
"repoBranch": "main"
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Place your project's logo (PNG or SVG) in this folder, then reference it from astro.config.mjs and src/content/docs/index.mdx.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Your Project Docs
|
|
3
|
+
description: Replace this with your project's elevator pitch — one sentence that tells visitors what you do.
|
|
4
|
+
template: splash
|
|
5
|
+
hero:
|
|
6
|
+
tagline: One-sentence tagline. Drop your hook here. The skill will offer to update this on first interaction.
|
|
7
|
+
actions:
|
|
8
|
+
- text: Get Started
|
|
9
|
+
link: /quickstart/
|
|
10
|
+
icon: right-arrow
|
|
11
|
+
variant: primary
|
|
12
|
+
- text: View Source
|
|
13
|
+
link: https://github.com/your-org/your-repo
|
|
14
|
+
icon: external
|
|
15
|
+
variant: secondary
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
|
|
19
|
+
import Glitch from '@abstractdata/starlight-theme/components/Glitch.astro';
|
|
20
|
+
|
|
21
|
+
## <Glitch text="WHAT'S INSIDE" />
|
|
22
|
+
|
|
23
|
+
Four cards is a starting point — replace, reorder, add more. Each `Card` accepts a `title`, an `icon` (any [Lucide icon](https://lucide.dev) name), and Markdown body content.
|
|
24
|
+
|
|
25
|
+
<CardGrid>
|
|
26
|
+
<Card title="Quickstart" icon="rocket">
|
|
27
|
+
The five-minute install + first deploy walkthrough. [Read it →](/quickstart/)
|
|
28
|
+
</Card>
|
|
29
|
+
<Card title="API Reference" icon="open-book">
|
|
30
|
+
Auto-generated from your source's docstrings (Python today; TypeScript and OpenAPI coming). Run `bun run docs:python`.
|
|
31
|
+
</Card>
|
|
32
|
+
<Card title="Brand toggle" icon="setting">
|
|
33
|
+
Two motion modes: `full` (HUD) for marketing-y docs, `calm` for prose-heavy client docs. Light + dark in both. Configure in `astro.config.mjs`.
|
|
34
|
+
</Card>
|
|
35
|
+
<Card title="Branded components" icon="puzzle">
|
|
36
|
+
`<Glitch>`, `<Hero>`, branded callouts, and a Shiki theme that paints code in cyan / gold / burgundy. Import from `@abstractdata/starlight-theme/components/*`.
|
|
37
|
+
</Card>
|
|
38
|
+
</CardGrid>
|
|
39
|
+
|
|
40
|
+
## Make it yours
|
|
41
|
+
|
|
42
|
+
Open Claude Code (or Cursor) in this folder — it'll greet you and offer to wire up Python autodoc, your sidebar, and your plugin config in one conversation. Or do it manually:
|
|
43
|
+
|
|
44
|
+
<CardGrid>
|
|
45
|
+
<LinkCard
|
|
46
|
+
title="Edit astro.config.mjs"
|
|
47
|
+
description="Set your project title, sidebar structure, social links, and which motion mode the brand runs in."
|
|
48
|
+
href="/quickstart/#configure"
|
|
49
|
+
/>
|
|
50
|
+
<LinkCard
|
|
51
|
+
title="Drop your logo"
|
|
52
|
+
description="Place it in src/assets/, then point logo.src in astro.config.mjs at it."
|
|
53
|
+
href="/quickstart/#logo"
|
|
54
|
+
/>
|
|
55
|
+
<LinkCard
|
|
56
|
+
title="Write content"
|
|
57
|
+
description="Markdown and MDX in src/content/docs/. Sub-folders become sidebar groups."
|
|
58
|
+
href="/quickstart/#content"
|
|
59
|
+
/>
|
|
60
|
+
<LinkCard
|
|
61
|
+
title="Deploy"
|
|
62
|
+
description="GitHub Pages workflow is preconfigured. Vercel and Cloudflare workflow variants ship alongside."
|
|
63
|
+
href="/quickstart/#deploy"
|
|
64
|
+
/>
|
|
65
|
+
</CardGrid>
|
|
66
|
+
|
|
67
|
+
## Branded callouts
|
|
68
|
+
|
|
69
|
+
The four standard Starlight callouts (`note`, `tip`, `caution`, `danger`) are styled with the brand palette — colored left border, matching glow in HUD mode, branded label typography.
|
|
70
|
+
|
|
71
|
+
:::tip[Pro tip]
|
|
72
|
+
Use `:::caution` for footguns, `:::danger` for destructive operations, `:::note` for context, `:::tip` for opinion. Your readers will thank you.
|
|
73
|
+
:::
|
|
74
|
+
|
|
75
|
+
:::caution[Heads up]
|
|
76
|
+
Update the placeholder values in `astro.config.mjs` (title, site URL, social links) before deploying. The skill's first-interaction handshake catches these — open your AI assistant if you'd like it to do the rewrite for you.
|
|
77
|
+
:::
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
> Replace this whole page once your real content lands. The `template: splash` frontmatter is what gives this hero its layout; switch to `template: doc` (or remove the line) on inner pages so they get a sidebar and table of contents.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Quickstart
|
|
3
|
+
description: Five minutes from clean checkout to a deployed branded docs site.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
This template gives you a Starlight documentation site pre-wired with the [Abstract Data Documentation Theme](https://github.com/Abstract-Data/abstract-data-doc-theme). This page is the hands-on walkthrough — copy it, replace the placeholders, ship it.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
bun install
|
|
12
|
+
bun dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
That boots the dev server at `http://localhost:4321` with hot module reload. Edit any `.md`/`.mdx` file under `src/content/docs/` and the page updates instantly.
|
|
16
|
+
|
|
17
|
+
:::tip
|
|
18
|
+
If you don't have Bun yet: `curl -fsSL https://bun.sh/install | bash`. The theme is designed for Bun (faster installs, native TypeScript) but works on `npm` and `pnpm` too if your team prefers.
|
|
19
|
+
:::
|
|
20
|
+
|
|
21
|
+
## Configure
|
|
22
|
+
|
|
23
|
+
Open `astro.config.mjs` and replace the placeholders:
|
|
24
|
+
|
|
25
|
+
```js {3-5,12,18-19}
|
|
26
|
+
starlight({
|
|
27
|
+
// ⬇️ project metadata
|
|
28
|
+
title: 'Your Project Docs',
|
|
29
|
+
description: 'One-sentence elevator pitch.',
|
|
30
|
+
site: 'https://your-deployed-url.example.com',
|
|
31
|
+
|
|
32
|
+
// ⬇️ uncomment for a logo
|
|
33
|
+
// logo: { src: './src/assets/your-logo.png', replacesTitle: true },
|
|
34
|
+
|
|
35
|
+
social: [
|
|
36
|
+
{
|
|
37
|
+
icon: 'github',
|
|
38
|
+
href: 'https://github.com/your-org/your-repo',
|
|
39
|
+
label: 'GitHub',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
|
|
43
|
+
plugins: [
|
|
44
|
+
abstractData({
|
|
45
|
+
motion: 'calm', // 'full' for HUD, 'calm' for client docs
|
|
46
|
+
credit: 'auto', // 'hide' for white-label
|
|
47
|
+
}),
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The three knobs that matter most:
|
|
53
|
+
|
|
54
|
+
- **`motion`** — `full` is the loud HUD look (marketing-y, animated hex grid, glitch on the version chip); `calm` strips animations while keeping palette and fonts. Default: `calm` for client work.
|
|
55
|
+
- **`credit`** — `auto` shows "Built by Abstract Data" in the footer; `hide` removes it for white-label projects.
|
|
56
|
+
- **`version`** *(optional)* — set to a string like `'v1.0.0'` to display a version chip in the header next to the social icons.
|
|
57
|
+
|
|
58
|
+
## Add your logo
|
|
59
|
+
|
|
60
|
+
Drop a PNG or SVG in `src/assets/` (e.g. `your-logo.png`), then in `astro.config.mjs`:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
logo: {
|
|
64
|
+
src: './src/assets/your-logo.png',
|
|
65
|
+
replacesTitle: true, // hide the text title when logo is present
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The theme's `<Hero>` component uses Astro's `<Image>` for optimized output (lazy loading, AVIF/WebP, srcset). Drop a `400×400` or larger source — Astro handles the rest.
|
|
70
|
+
|
|
71
|
+
## Write content
|
|
72
|
+
|
|
73
|
+
Pages live under `src/content/docs/`:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
src/content/docs/
|
|
77
|
+
├── index.mdx # the splash page (this file is its sibling)
|
|
78
|
+
├── quickstart.md # this page
|
|
79
|
+
└── guides/ # subfolders become sidebar groups (with autogenerate)
|
|
80
|
+
├── deployment.md
|
|
81
|
+
└── customization.md
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Frontmatter sets the page title and description. Body is Markdown or MDX (Markdown + components):
|
|
85
|
+
|
|
86
|
+
```md
|
|
87
|
+
---
|
|
88
|
+
title: My Page
|
|
89
|
+
description: Short SEO description.
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
# Heading auto-rendered by Starlight
|
|
93
|
+
|
|
94
|
+
Markdown content with **bold**, *italic*, [links](/), and `inline code`.
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The theme's branded components are importable in any `.mdx` file:
|
|
98
|
+
|
|
99
|
+
```mdx
|
|
100
|
+
import Glitch from '@abstractdata/starlight-theme/components/Glitch.astro';
|
|
101
|
+
|
|
102
|
+
# <Glitch text="404" /> Not Found
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Add API reference
|
|
106
|
+
|
|
107
|
+
The template ships two autodoc pipelines — pick the one(s) that match your source. The `abstract-data-setup` workflow can detect your stack and prune this section automatically; if you'd rather drive it manually, follow whichever subsection applies.
|
|
108
|
+
|
|
109
|
+
<!-- abstract-data-setup:python-autodoc -->
|
|
110
|
+
|
|
111
|
+
### Python source
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
bun run docs:python
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Reads `scripts/python-autodoc.json`, generates Markdown pages under `src/content/docs/api/` from your module docstrings via `pydoc-markdown`. Edit the config to point `searchPath` at your Python source root and list the `modules` you want documented.
|
|
118
|
+
|
|
119
|
+
:::caution
|
|
120
|
+
You need `pydoc-markdown` installed in your local Python environment: `pipx install pydoc-markdown` or `pip install --user pydoc-markdown`.
|
|
121
|
+
:::
|
|
122
|
+
|
|
123
|
+
<!-- /abstract-data-setup:python-autodoc -->
|
|
124
|
+
|
|
125
|
+
<!-- abstract-data-setup:ts-autodoc -->
|
|
126
|
+
|
|
127
|
+
### TypeScript source
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
bun run docs:ts
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Reads `scripts/ts-autodoc.json`, generates Markdown pages under `src/content/docs/api/ts/` from your TSDoc comments via TypeDoc + `typedoc-plugin-markdown`. Point `entryPoints` at the entry TS files and `tsconfig` at the matching tsconfig.
|
|
134
|
+
|
|
135
|
+
:::caution
|
|
136
|
+
Install once as dev deps: `bun add -d typedoc typedoc-plugin-markdown`.
|
|
137
|
+
:::
|
|
138
|
+
|
|
139
|
+
<!-- /abstract-data-setup:ts-autodoc -->
|
|
140
|
+
|
|
141
|
+
:::tip[Guided setup]
|
|
142
|
+
Open Claude Code or Cursor in this folder and say *"set up docs"* — the bundled `abstract-data-setup` workflow detects your stack(s), audits docstring coverage, asks which modules to document, writes the configs, and tailors this very page so only the relevant autodoc subsection remains.
|
|
143
|
+
:::
|
|
144
|
+
|
|
145
|
+
## Deploy
|
|
146
|
+
|
|
147
|
+
Three workflow files ship in `.github/workflows/`:
|
|
148
|
+
|
|
149
|
+
| File | Target | When to use |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `deploy.yml` | GitHub Pages | Default. Just enable Pages → "GitHub Actions" in repo settings and push. |
|
|
152
|
+
| `deploy-vercel.yml` | Vercel | Opt-in. Add `VERCEL_TOKEN`, `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID` secrets; run once. |
|
|
153
|
+
| `deploy-cloudflare.yml` | Cloudflare Pages | Opt-in. Add `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`. |
|
|
154
|
+
|
|
155
|
+
For Vercel and Cloudflare, the simpler path is connecting the repo through their respective dashboards (no workflow file needed) — they auto-detect Astro and deploy on push. Use the workflow files only when you want CI to control deploys.
|
|
156
|
+
|
|
157
|
+
## What's next
|
|
158
|
+
|
|
159
|
+
- Iterate on `index.mdx` — the splash page is your shop window.
|
|
160
|
+
- Add per-section guides under `src/content/docs/guides/`.
|
|
161
|
+
- Run `bun run build && bun run preview` before deploying to catch errors with production assets.
|
|
162
|
+
- Open Claude Code or Cursor in this folder if you want a guided setup — the bundled `abstract-data-setup` workflow handles the boilerplate.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { defineCollection, z } from 'astro:content';
|
|
2
|
+
import { docsLoader } from '@astrojs/starlight/loaders';
|
|
3
|
+
import { docsSchema } from '@astrojs/starlight/schema';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Type-safe frontmatter for the docs collection.
|
|
7
|
+
*
|
|
8
|
+
* `docsSchema({ extend })` lets you add custom fields on top of Starlight's
|
|
9
|
+
* built-in ones (`title`, `description`, `sidebar`, `draft`, `pagefind`, …).
|
|
10
|
+
* The fields below are scaffolding — keep, replace, or extend them to match
|
|
11
|
+
* how your team actually tags content. Errors in frontmatter fail the build
|
|
12
|
+
* with helpful Zod messages, so this catches issues at PR time instead of
|
|
13
|
+
* after deploy.
|
|
14
|
+
*
|
|
15
|
+
* Common fields teams add:
|
|
16
|
+
* - `category`: a grouping label for filterable views or future analytics
|
|
17
|
+
* - `audience`: who this page is written for
|
|
18
|
+
* - `lastReviewed`: when a maintainer last vetted the content (good for
|
|
19
|
+
* surfacing "this page is over a year old" warnings)
|
|
20
|
+
*/
|
|
21
|
+
export const collections = {
|
|
22
|
+
docs: defineCollection({
|
|
23
|
+
loader: docsLoader(),
|
|
24
|
+
schema: docsSchema({
|
|
25
|
+
extend: z.object({
|
|
26
|
+
// ── User-authored fields ───────────────────────────────────
|
|
27
|
+
category: z.string().optional(),
|
|
28
|
+
audience: z
|
|
29
|
+
.enum(['user', 'contributor', 'maintainer'])
|
|
30
|
+
.optional(),
|
|
31
|
+
lastReviewed: z.coerce.date().optional(),
|
|
32
|
+
|
|
33
|
+
// ── Theme-managed fields ───────────────────────────────────
|
|
34
|
+
// Written by the autodoc orchestrators (build-python-docs.mjs /
|
|
35
|
+
// build-ts-docs.mjs) onto every page emitted under a `versions[]`
|
|
36
|
+
// tag. The bundled <VersionPicker> reads them via
|
|
37
|
+
// getCollection('docs') so the autodoc JSON stays the canonical
|
|
38
|
+
// source of truth — no duplicating the version list in your
|
|
39
|
+
// override component. Don't hand-edit these.
|
|
40
|
+
version: z.string().optional(),
|
|
41
|
+
versionLabel: z.string().optional(),
|
|
42
|
+
versionDefault: z.boolean().optional(),
|
|
43
|
+
}),
|
|
44
|
+
}),
|
|
45
|
+
}),
|
|
46
|
+
};
|