rspress-plugin-api-extractor 0.1.0
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/0~llms-program.js +344 -0
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/index.d.ts +831 -0
- package/index.js +6333 -0
- package/package.json +75 -0
- package/tsdoc-metadata.json +11 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import node_path from "node:path";
|
|
2
|
+
import { FileSystem } from "@effect/platform";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
const LLMS_TXT_LINE_RE = /^-\s+\[([^\]]+)\]\(([^)]+)\)(?::\s*(.+))?$/;
|
|
5
|
+
function parseLlmsTxtLine(line) {
|
|
6
|
+
const trimmed = line.trim();
|
|
7
|
+
if ("" === trimmed) return null;
|
|
8
|
+
const match = LLMS_TXT_LINE_RE.exec(trimmed);
|
|
9
|
+
if (!match) return null;
|
|
10
|
+
const title = match[1];
|
|
11
|
+
const url = match[2];
|
|
12
|
+
const rawDescription = match[3];
|
|
13
|
+
return {
|
|
14
|
+
title,
|
|
15
|
+
url,
|
|
16
|
+
description: rawDescription ? rawDescription.trim() : void 0
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function filterLlmsTxt(content, apiRoutes, pointers) {
|
|
20
|
+
const lines = content.split("\n");
|
|
21
|
+
const filtered = [];
|
|
22
|
+
for (const line of lines){
|
|
23
|
+
const entry = parseLlmsTxtLine(line);
|
|
24
|
+
if (!(entry && apiRoutes.has(entry.url))) filtered.push(line);
|
|
25
|
+
}
|
|
26
|
+
let result = filtered.join("\n");
|
|
27
|
+
if (pointers.length > 0) {
|
|
28
|
+
result += "\n\n";
|
|
29
|
+
for (const pointer of pointers)result += `- For ${pointer.name} API docs, see [${pointer.name} llms.txt](${pointer.llmsTxtUrl})\n`;
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function generateStructuredLlmsTxt(content, apiRoutes, packages) {
|
|
34
|
+
const lines = content.split("\n");
|
|
35
|
+
let title = "";
|
|
36
|
+
for (const line of lines)if (line.startsWith("# ")) {
|
|
37
|
+
title = line;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
const allEntries = [];
|
|
41
|
+
for (const line of lines){
|
|
42
|
+
const entry = parseLlmsTxtLine(line);
|
|
43
|
+
if (entry && !apiRoutes.has(entry.url)) allEntries.push(entry);
|
|
44
|
+
}
|
|
45
|
+
const packageEntries = new Map();
|
|
46
|
+
const others = [];
|
|
47
|
+
for (const entry of allEntries){
|
|
48
|
+
let matched = false;
|
|
49
|
+
for (const pkg of packages){
|
|
50
|
+
const base = pkg.packageRoute.endsWith("/") ? pkg.packageRoute : `${pkg.packageRoute}/`;
|
|
51
|
+
if (entry.url.startsWith(base) || entry.url === pkg.packageRoute) {
|
|
52
|
+
const existing = packageEntries.get(pkg.packageName) ?? [];
|
|
53
|
+
existing.push(entry);
|
|
54
|
+
packageEntries.set(pkg.packageName, existing);
|
|
55
|
+
matched = true;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!matched) others.push(entry);
|
|
60
|
+
}
|
|
61
|
+
const output = [];
|
|
62
|
+
if (title) {
|
|
63
|
+
output.push(title);
|
|
64
|
+
output.push("");
|
|
65
|
+
}
|
|
66
|
+
if (others.length > 0) {
|
|
67
|
+
output.push("## Others");
|
|
68
|
+
output.push("");
|
|
69
|
+
for (const entry of others)output.push(formatEntry(entry));
|
|
70
|
+
output.push("");
|
|
71
|
+
}
|
|
72
|
+
const packagesWithEntries = packages;
|
|
73
|
+
if (packagesWithEntries.length > 0) {
|
|
74
|
+
output.push("## Packages");
|
|
75
|
+
output.push("");
|
|
76
|
+
for (const pkg of packagesWithEntries){
|
|
77
|
+
const versionSuffix = pkg.version ? ` ${pkg.version}` : "";
|
|
78
|
+
output.push(`### ${pkg.name}${versionSuffix}`);
|
|
79
|
+
output.push("");
|
|
80
|
+
if (pkg.description) {
|
|
81
|
+
output.push(pkg.description);
|
|
82
|
+
output.push("");
|
|
83
|
+
}
|
|
84
|
+
const entries = packageEntries.get(pkg.packageName) ?? [];
|
|
85
|
+
for (const entry of entries)output.push(formatEntry(entry));
|
|
86
|
+
output.push(`- [API Reference](${pkg.llmsApiTxtUrl})`);
|
|
87
|
+
output.push("");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return output.join("\n");
|
|
91
|
+
}
|
|
92
|
+
function parseSections(content) {
|
|
93
|
+
if ("" === content.trim()) return [];
|
|
94
|
+
const sections = [];
|
|
95
|
+
const frontmatterPattern = /^---\nurl:\s*(.+)\n---$/gm;
|
|
96
|
+
let match = frontmatterPattern.exec(content);
|
|
97
|
+
const boundaries = [];
|
|
98
|
+
while(null !== match){
|
|
99
|
+
boundaries.push({
|
|
100
|
+
url: match[1].trim(),
|
|
101
|
+
start: match.index,
|
|
102
|
+
fmEnd: match.index + match[0].length
|
|
103
|
+
});
|
|
104
|
+
match = frontmatterPattern.exec(content);
|
|
105
|
+
}
|
|
106
|
+
for(let i = 0; i < boundaries.length; i++){
|
|
107
|
+
const boundary = boundaries[i];
|
|
108
|
+
const nextStart = i + 1 < boundaries.length ? boundaries[i + 1].start : content.length;
|
|
109
|
+
const sectionContent = content.slice(boundary.start, nextStart);
|
|
110
|
+
sections.push({
|
|
111
|
+
url: boundary.url,
|
|
112
|
+
raw: sectionContent.trimEnd()
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return sections;
|
|
116
|
+
}
|
|
117
|
+
function filterLlmsFullTxt(content, apiRoutes) {
|
|
118
|
+
if ("" === content.trim()) return "";
|
|
119
|
+
const sections = parseSections(content);
|
|
120
|
+
const kept = sections.filter((section)=>!apiRoutes.has(section.url));
|
|
121
|
+
if (0 === kept.length) return "";
|
|
122
|
+
return kept.map((section)=>section.raw).join("\n\n\n");
|
|
123
|
+
}
|
|
124
|
+
function formatEntry(entry) {
|
|
125
|
+
if (entry.description) return `- [${entry.title}](${entry.url}): ${entry.description}`;
|
|
126
|
+
return `- [${entry.title}](${entry.url})`;
|
|
127
|
+
}
|
|
128
|
+
function generatePackageLlmsTxt(input) {
|
|
129
|
+
const parts = [
|
|
130
|
+
`# ${input.name}`,
|
|
131
|
+
"",
|
|
132
|
+
`> API documentation for the ${input.packageName} package`
|
|
133
|
+
];
|
|
134
|
+
if (input.guidePages.length > 0) {
|
|
135
|
+
parts.push("");
|
|
136
|
+
parts.push("## Guides");
|
|
137
|
+
parts.push("");
|
|
138
|
+
for (const page of input.guidePages)parts.push(formatEntry(page));
|
|
139
|
+
}
|
|
140
|
+
if (input.apiPages.length > 0) {
|
|
141
|
+
parts.push("");
|
|
142
|
+
parts.push("## API Reference");
|
|
143
|
+
parts.push("");
|
|
144
|
+
for (const page of input.apiPages)parts.push(formatEntry(page));
|
|
145
|
+
}
|
|
146
|
+
parts.push("");
|
|
147
|
+
return parts.join("\n");
|
|
148
|
+
}
|
|
149
|
+
function generatePackageLlmsFullTxt(pages) {
|
|
150
|
+
if (0 === pages.length) return "";
|
|
151
|
+
const sections = [];
|
|
152
|
+
for (const page of pages)sections.push(`---\nurl: ${page.url}\n---\n\n${page.content}`);
|
|
153
|
+
return sections.join("\n\n\n");
|
|
154
|
+
}
|
|
155
|
+
function buildApiRoutes(buildResults) {
|
|
156
|
+
const apiRoutes = new Set();
|
|
157
|
+
for (const result of buildResults){
|
|
158
|
+
const base = result.baseRoute.endsWith("/") ? result.baseRoute : `${result.baseRoute}/`;
|
|
159
|
+
for (const relPath of result.generatedFiles){
|
|
160
|
+
if (!relPath.endsWith(".mdx")) continue;
|
|
161
|
+
const mdPath = relPath.replace(/\.mdx$/, ".md");
|
|
162
|
+
const routeUrl = `${base}${mdPath}`;
|
|
163
|
+
apiRoutes.add(routeUrl);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return apiRoutes;
|
|
167
|
+
}
|
|
168
|
+
function discoverPrefixes(buildResults) {
|
|
169
|
+
const prefixes = new Set();
|
|
170
|
+
prefixes.add("");
|
|
171
|
+
for (const result of buildResults){
|
|
172
|
+
const segments = result.baseRoute.split("/").filter(Boolean);
|
|
173
|
+
if (segments.length > 1) {
|
|
174
|
+
const prefixSegments = segments.slice(0, -1);
|
|
175
|
+
prefixes.add(prefixSegments.join("/"));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return prefixes;
|
|
179
|
+
}
|
|
180
|
+
function buildPackagePointers(buildResults, prefix, packageRoutes) {
|
|
181
|
+
const pointers = [];
|
|
182
|
+
for (const result of buildResults){
|
|
183
|
+
if ("" !== prefix && !result.baseRoute.startsWith(`/${prefix}/`)) continue;
|
|
184
|
+
const displayName = result.apiName ?? result.packageName;
|
|
185
|
+
const pkgRoute = packageRoutes.get(result.packageName) ?? result.baseRoute;
|
|
186
|
+
const base = pkgRoute.endsWith("/") ? pkgRoute : `${pkgRoute}/`;
|
|
187
|
+
pointers.push({
|
|
188
|
+
name: displayName,
|
|
189
|
+
llmsTxtUrl: `${base}llms.txt`
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return pointers;
|
|
193
|
+
}
|
|
194
|
+
function collectApiEntries(globalLlmsTxtContent, result) {
|
|
195
|
+
const base = result.baseRoute.endsWith("/") ? result.baseRoute : `${result.baseRoute}/`;
|
|
196
|
+
const entries = [];
|
|
197
|
+
for (const line of globalLlmsTxtContent.split("\n")){
|
|
198
|
+
const entry = parseLlmsTxtLine(line);
|
|
199
|
+
if (entry) {
|
|
200
|
+
if (entry.url.startsWith(base)) entries.push(entry);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return entries;
|
|
204
|
+
}
|
|
205
|
+
function collectGuideEntries(globalLlmsTxtContent, apiRoutes, packageRoute) {
|
|
206
|
+
const base = packageRoute.endsWith("/") ? packageRoute : `${packageRoute}/`;
|
|
207
|
+
const entries = [];
|
|
208
|
+
for (const line of globalLlmsTxtContent.split("\n")){
|
|
209
|
+
const entry = parseLlmsTxtLine(line);
|
|
210
|
+
if (entry) {
|
|
211
|
+
if ((entry.url === packageRoute || entry.url.startsWith(base)) && !apiRoutes.has(entry.url)) entries.push(entry);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return entries;
|
|
215
|
+
}
|
|
216
|
+
function extractSections(globalLlmsFullContent, urlPredicate) {
|
|
217
|
+
const pages = [];
|
|
218
|
+
if (!globalLlmsFullContent) return pages;
|
|
219
|
+
const frontmatterPattern = /^---\nurl:\s*(.+)\n---$/gm;
|
|
220
|
+
let match = frontmatterPattern.exec(globalLlmsFullContent);
|
|
221
|
+
const boundaries = [];
|
|
222
|
+
while(null !== match){
|
|
223
|
+
boundaries.push({
|
|
224
|
+
url: match[1].trim(),
|
|
225
|
+
start: match.index,
|
|
226
|
+
fmEnd: match.index + match[0].length
|
|
227
|
+
});
|
|
228
|
+
match = frontmatterPattern.exec(globalLlmsFullContent);
|
|
229
|
+
}
|
|
230
|
+
for(let i = 0; i < boundaries.length; i++){
|
|
231
|
+
const boundary = boundaries[i];
|
|
232
|
+
if (!urlPredicate(boundary.url)) continue;
|
|
233
|
+
const nextStart = i + 1 < boundaries.length ? boundaries[i + 1].start : globalLlmsFullContent.length;
|
|
234
|
+
const content = globalLlmsFullContent.slice(boundary.fmEnd, nextStart).trim();
|
|
235
|
+
pages.push({
|
|
236
|
+
url: boundary.url,
|
|
237
|
+
content
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return pages;
|
|
241
|
+
}
|
|
242
|
+
function collectApiPageContent(globalLlmsFullContent, result) {
|
|
243
|
+
const base = result.baseRoute.endsWith("/") ? result.baseRoute : `${result.baseRoute}/`;
|
|
244
|
+
return extractSections(globalLlmsFullContent, (url)=>url.startsWith(base));
|
|
245
|
+
}
|
|
246
|
+
function processLlmsFiles(input) {
|
|
247
|
+
return Effect.gen(function*() {
|
|
248
|
+
const fs = yield* FileSystem.FileSystem;
|
|
249
|
+
const { outDir, buildResults, llmsPlugin, packageRoutes } = input;
|
|
250
|
+
if (0 === buildResults.length) return;
|
|
251
|
+
const apiRoutes = buildApiRoutes(buildResults);
|
|
252
|
+
yield* Effect.logDebug(`Built ${apiRoutes.size} API routes for LLMs filtering`);
|
|
253
|
+
if (0 === apiRoutes.size) return;
|
|
254
|
+
const prefixes = discoverPrefixes(buildResults);
|
|
255
|
+
yield* Effect.forEach([
|
|
256
|
+
...prefixes
|
|
257
|
+
], (prefix)=>processPrefix(fs, outDir, prefix, buildResults, apiRoutes, llmsPlugin, packageRoutes), {
|
|
258
|
+
concurrency: "unbounded"
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function processPrefix(fs, outDir, prefix, buildResults, apiRoutes, llmsPlugin, packageRoutes) {
|
|
263
|
+
return Effect.gen(function*() {
|
|
264
|
+
const prefixDir = prefix ? node_path.join(outDir, prefix) : outDir;
|
|
265
|
+
const llmsTxtPath = node_path.join(prefixDir, "llms.txt");
|
|
266
|
+
const llmsTxtExists = yield* fs.exists(llmsTxtPath).pipe(Effect.orElseSucceed(()=>false));
|
|
267
|
+
if (!llmsTxtExists) return;
|
|
268
|
+
const llmsTxtContent = yield* fs.readFileString(llmsTxtPath).pipe(Effect.orDie);
|
|
269
|
+
const llmsFullTxtPath = node_path.join(prefixDir, "llms-full.txt");
|
|
270
|
+
const llmsFullTxtExists = yield* fs.exists(llmsFullTxtPath).pipe(Effect.orElseSucceed(()=>false));
|
|
271
|
+
const llmsFullTxtContent = llmsFullTxtExists ? yield* fs.readFileString(llmsFullTxtPath).pipe(Effect.orDie) : "";
|
|
272
|
+
if (llmsPlugin.scopes) {
|
|
273
|
+
const packageScopes = buildResults.map((r)=>{
|
|
274
|
+
const pkgRoute = packageRoutes.get(r.packageName) ?? r.baseRoute;
|
|
275
|
+
return {
|
|
276
|
+
name: r.apiName ?? r.packageName,
|
|
277
|
+
packageName: r.packageName,
|
|
278
|
+
version: r.packageVersion,
|
|
279
|
+
description: r.packageDescription,
|
|
280
|
+
packageRoute: pkgRoute,
|
|
281
|
+
llmsApiTxtUrl: `${pkgRoute.endsWith("/") ? pkgRoute : `${pkgRoute}/`}llms-api.txt`
|
|
282
|
+
};
|
|
283
|
+
});
|
|
284
|
+
const structuredLlmsTxt = generateStructuredLlmsTxt(llmsTxtContent, apiRoutes, packageScopes);
|
|
285
|
+
yield* fs.writeFileString(llmsTxtPath, structuredLlmsTxt).pipe(Effect.orDie);
|
|
286
|
+
} else {
|
|
287
|
+
const pointers = buildPackagePointers(buildResults, prefix, packageRoutes);
|
|
288
|
+
const filteredLlmsTxt = filterLlmsTxt(llmsTxtContent, apiRoutes, pointers);
|
|
289
|
+
yield* fs.writeFileString(llmsTxtPath, filteredLlmsTxt).pipe(Effect.orDie);
|
|
290
|
+
}
|
|
291
|
+
if (llmsFullTxtContent) {
|
|
292
|
+
const filteredLlmsFullTxt = filterLlmsFullTxt(llmsFullTxtContent, apiRoutes);
|
|
293
|
+
yield* fs.writeFileString(llmsFullTxtPath, filteredLlmsFullTxt).pipe(Effect.orDie);
|
|
294
|
+
}
|
|
295
|
+
if (llmsPlugin.scopes) {
|
|
296
|
+
const prefixResults = "" === prefix ? [
|
|
297
|
+
...buildResults
|
|
298
|
+
] : buildResults.filter((r)=>r.baseRoute.startsWith(`/${prefix}/`));
|
|
299
|
+
yield* Effect.forEach(prefixResults, (result)=>generatePerPackageFiles(fs, outDir, result, llmsTxtContent, llmsFullTxtContent, apiRoutes, llmsPlugin, packageRoutes.get(result.packageName) ?? result.baseRoute), {
|
|
300
|
+
concurrency: "unbounded"
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
function generatePerPackageFiles(fs, outDir, result, globalLlmsTxtContent, globalLlmsFullContent, apiRoutes, llmsPlugin, packageRoute) {
|
|
306
|
+
return Effect.gen(function*() {
|
|
307
|
+
const pkgRouteSegment = packageRoute.replace(/^\//, "");
|
|
308
|
+
const packageLlmsDir = pkgRouteSegment ? node_path.join(outDir, pkgRouteSegment) : outDir;
|
|
309
|
+
yield* fs.makeDirectory(packageLlmsDir, {
|
|
310
|
+
recursive: true
|
|
311
|
+
}).pipe(Effect.orDie);
|
|
312
|
+
const displayName = result.apiName ?? result.packageName;
|
|
313
|
+
const apiEntries = collectApiEntries(globalLlmsTxtContent, result);
|
|
314
|
+
const guideEntries = collectGuideEntries(globalLlmsTxtContent, apiRoutes, packageRoute);
|
|
315
|
+
const packageLlmsTxt = generatePackageLlmsTxt({
|
|
316
|
+
name: displayName,
|
|
317
|
+
packageName: result.packageName,
|
|
318
|
+
guidePages: guideEntries,
|
|
319
|
+
apiPages: apiEntries
|
|
320
|
+
});
|
|
321
|
+
yield* fs.writeFileString(node_path.join(packageLlmsDir, "llms.txt"), packageLlmsTxt).pipe(Effect.orDie);
|
|
322
|
+
const apiPageContent = collectApiPageContent(globalLlmsFullContent, result);
|
|
323
|
+
const guideRouteUrls = new Set(guideEntries.map((e)=>e.url));
|
|
324
|
+
const guidePageContent = globalLlmsFullContent ? extractSections(globalLlmsFullContent, (url)=>guideRouteUrls.has(url)) : [];
|
|
325
|
+
const fullPageContent = [
|
|
326
|
+
...guidePageContent,
|
|
327
|
+
...apiPageContent
|
|
328
|
+
];
|
|
329
|
+
if (fullPageContent.length > 0) {
|
|
330
|
+
const packageLlmsFullTxt = generatePackageLlmsFullTxt(fullPageContent);
|
|
331
|
+
yield* fs.writeFileString(node_path.join(packageLlmsDir, "llms-full.txt"), packageLlmsFullTxt).pipe(Effect.orDie);
|
|
332
|
+
}
|
|
333
|
+
if (llmsPlugin.apiTxt && apiPageContent.length > 0) {
|
|
334
|
+
const apiTxtContent = generatePackageLlmsFullTxt(apiPageContent);
|
|
335
|
+
yield* fs.writeFileString(node_path.join(packageLlmsDir, "llms-api.txt"), apiTxtContent).pipe(Effect.orDie);
|
|
336
|
+
}
|
|
337
|
+
if (guidePageContent.length > 0) {
|
|
338
|
+
const docsTxtContent = generatePackageLlmsFullTxt(guidePageContent);
|
|
339
|
+
yield* fs.writeFileString(node_path.join(packageLlmsDir, "llms-docs.txt"), docsTxtContent).pipe(Effect.orDie);
|
|
340
|
+
}
|
|
341
|
+
yield* Effect.logDebug(`Generated LLMs files for ${displayName} in ${packageLlmsDir}`);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
export { processLlmsFiles };
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 C. Spencer Beggs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# rspress-plugin-api-extractor
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/rspress-plugin-api-extractor)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+
An [RSPress](https://rspress.dev/) 2.0 plugin that generates interactive API documentation from [Microsoft API Extractor](https://api-extractor.com/) models. Point it at your `.api.json` files and you get a documentation site: syntax-highlighted signatures, Twoslash hover tooltips, type references that cross-link between pages and copy-paste code examples.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install rspress-plugin-api-extractor
|
|
14
|
+
# or
|
|
15
|
+
pnpm add rspress-plugin-api-extractor
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The plugin is a peer of `@rspress/core`, `react` and `react-dom`; install those too if your site does not already have them.
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// rspress.config.ts
|
|
24
|
+
import { defineConfig } from "@rspress/core";
|
|
25
|
+
import { ApiExtractorPlugin } from "rspress-plugin-api-extractor";
|
|
26
|
+
|
|
27
|
+
export default defineConfig({
|
|
28
|
+
root: "docs",
|
|
29
|
+
plugins: [
|
|
30
|
+
ApiExtractorPlugin({
|
|
31
|
+
api: {
|
|
32
|
+
packageName: "my-library",
|
|
33
|
+
model: "./api/my-library.api.json",
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx rspress dev
|
|
42
|
+
# Generates one MDX page per public API item and serves them at http://localhost:3000
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The plugin reads your `.api.json` model and writes one MDX page per public API item under your docs root, grouped into category folders (classes, interfaces, functions, type aliases, enums, variables and namespaces) with navigation metadata. To produce the model, pair it with [@savvy-web/rslib-builder](https://github.com/savvy-web/rslib-builder), which emits the `.api.json` as part of your TypeScript build.
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- Generates API docs from `.api.json` models for classes, interfaces, functions, type aliases, enums, variables and namespaces.
|
|
50
|
+
- Type-checks code examples and adds Twoslash hover tooltips that show inferred types.
|
|
51
|
+
- Cross-links type references between pages, so a type named in a signature links to its own page.
|
|
52
|
+
- Drives single-package sites, multi-package portals, RSPress multiVersion and i18n from one plugin.
|
|
53
|
+
- Handles multi-entry-point packages: it deduplicates re-exports and notes which entry points each item is available from.
|
|
54
|
+
- Writes per-package `llms*.txt` files and in-page actions for pointing an assistant at one package's docs.
|
|
55
|
+
|
|
56
|
+
## Documentation
|
|
57
|
+
|
|
58
|
+
- [Getting started](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/01-getting-started.md) — Install, minimal config, first build.
|
|
59
|
+
- [Configuration](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/02-configuration.md) — Full plugin-options reference.
|
|
60
|
+
- [Config helpers](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/03-config-helpers.md) — `fromFolder` and `fromModelsDir` for discovering config from package folders.
|
|
61
|
+
- [Single package](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/04-single-package.md) — The single-API recipe.
|
|
62
|
+
- [Multi-package](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/05-multi-package.md) — The multi-API portal recipe.
|
|
63
|
+
- [Versioned](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/06-versioned.md) — Documenting major versions side by side.
|
|
64
|
+
- [i18n](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/07-i18n.md) — Internationalized documentation.
|
|
65
|
+
- [Multi-entry points](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/08-multi-entry-points.md) — Deduplication, "Available from" and route collisions.
|
|
66
|
+
- [LLMs](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/09-llms.md) — Per-package `llms*.txt` files and assistant actions.
|
|
67
|
+
- [Runtime components](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/10-runtime-components.md) — The runtime components and live `with-api` code blocks.
|
|
68
|
+
- [Troubleshooting](https://github.com/spencerbeggs/rspress-plugin-api-extractor/blob/main/docs/11-troubleshooting.md) — Route collisions, forgotten exports, Twoslash errors and stale caches.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
[MIT](LICENSE)
|