skrypt-ai 0.3.4 → 0.4.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/README.md +1 -1
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/index.js +3 -5
- package/dist/autofix/index.js +15 -3
- package/dist/cli.js +19 -4
- package/dist/commands/check-links.js +164 -174
- package/dist/commands/deploy.js +5 -2
- package/dist/commands/generate.js +206 -199
- package/dist/commands/i18n.js +3 -20
- package/dist/commands/init.js +47 -40
- package/dist/commands/lint.js +3 -20
- package/dist/commands/mcp.js +125 -122
- package/dist/commands/monitor.js +125 -108
- package/dist/commands/review-pr.js +1 -1
- package/dist/commands/sdk.js +1 -1
- package/dist/config/loader.js +21 -2
- package/dist/generator/organizer.d.ts +3 -0
- package/dist/generator/organizer.js +4 -9
- package/dist/generator/writer.js +2 -10
- package/dist/github/pr-comments.js +21 -8
- package/dist/plugins/index.js +1 -0
- package/dist/scanner/index.js +8 -2
- package/dist/template/docs.json +2 -1
- package/dist/template/next.config.mjs +2 -1
- package/dist/template/package.json +17 -15
- package/dist/template/public/favicon.svg +4 -0
- package/dist/template/public/search-index.json +1 -1
- package/dist/template/scripts/build-search-index.mjs +120 -25
- package/dist/template/src/app/api/chat/route.ts +11 -3
- package/dist/template/src/app/docs/README.md +28 -0
- package/dist/template/src/app/docs/[...slug]/page.tsx +139 -16
- package/dist/template/src/app/docs/auth/page.mdx +589 -0
- package/dist/template/src/app/docs/autofix/page.mdx +624 -0
- package/dist/template/src/app/docs/cli/page.mdx +217 -0
- package/dist/template/src/app/docs/config/page.mdx +428 -0
- package/dist/template/src/app/docs/configuration/page.mdx +86 -0
- package/dist/template/src/app/docs/deployment/page.mdx +112 -0
- package/dist/template/src/app/docs/error.tsx +20 -0
- package/dist/template/src/app/docs/generator/generator.md +504 -0
- package/dist/template/src/app/docs/generator/organizer.md +779 -0
- package/dist/template/src/app/docs/generator/page.mdx +613 -0
- package/dist/template/src/app/docs/github/page.mdx +502 -0
- package/dist/template/src/app/docs/llm/anthropic-client.md +549 -0
- package/dist/template/src/app/docs/llm/index.md +471 -0
- package/dist/template/src/app/docs/llm/page.mdx +428 -0
- package/dist/template/src/app/docs/llms-full.md +256 -0
- package/dist/template/src/app/docs/llms.txt +2971 -0
- package/dist/template/src/app/docs/not-found.tsx +23 -0
- package/dist/template/src/app/docs/page.mdx +0 -3
- package/dist/template/src/app/docs/plugins/page.mdx +1793 -0
- package/dist/template/src/app/docs/pro/page.mdx +121 -0
- package/dist/template/src/app/docs/quickstart/page.mdx +93 -0
- package/dist/template/src/app/docs/scanner/content-type.md +599 -0
- package/dist/template/src/app/docs/scanner/index.md +212 -0
- package/dist/template/src/app/docs/scanner/page.mdx +307 -0
- package/dist/template/src/app/docs/scanner/python.md +469 -0
- package/dist/template/src/app/docs/scanner/python_parser.md +1056 -0
- package/dist/template/src/app/docs/scanner/rust.md +325 -0
- package/dist/template/src/app/docs/scanner/typescript.md +201 -0
- package/dist/template/src/app/error.tsx +3 -3
- package/dist/template/src/app/icon.tsx +29 -0
- package/dist/template/src/app/layout.tsx +42 -0
- package/dist/template/src/app/not-found.tsx +35 -0
- package/dist/template/src/app/page.tsx +62 -28
- package/dist/template/src/components/ai-chat.tsx +26 -21
- package/dist/template/src/components/breadcrumbs.tsx +46 -2
- package/dist/template/src/components/copy-button.tsx +17 -3
- package/dist/template/src/components/docs-layout.tsx +142 -8
- package/dist/template/src/components/feedback.tsx +4 -2
- package/dist/template/src/components/footer.tsx +42 -0
- package/dist/template/src/components/header.tsx +29 -5
- package/dist/template/src/components/mdx/accordion.tsx +7 -6
- package/dist/template/src/components/mdx/card.tsx +19 -7
- package/dist/template/src/components/mdx/code-block.tsx +17 -3
- package/dist/template/src/components/mdx/code-group.tsx +65 -18
- package/dist/template/src/components/mdx/code-playground.tsx +3 -0
- package/dist/template/src/components/mdx/go-playground.tsx +3 -0
- package/dist/template/src/components/mdx/highlighted-code.tsx +171 -76
- package/dist/template/src/components/mdx/python-playground.tsx +2 -0
- package/dist/template/src/components/mdx/tabs.tsx +74 -6
- package/dist/template/src/components/page-header.tsx +19 -0
- package/dist/template/src/components/scroll-to-top.tsx +33 -0
- package/dist/template/src/components/search-dialog.tsx +206 -52
- package/dist/template/src/components/sidebar.tsx +136 -77
- package/dist/template/src/components/table-of-contents.tsx +23 -7
- package/dist/template/src/lib/highlight.ts +90 -31
- package/dist/template/src/lib/search.ts +14 -4
- package/dist/template/src/lib/theme-utils.ts +140 -0
- package/dist/template/src/styles/globals.css +307 -166
- package/dist/template/src/types/remark-gfm.d.ts +2 -0
- package/dist/utils/files.d.ts +9 -0
- package/dist/utils/files.js +33 -0
- package/dist/utils/validation.d.ts +4 -0
- package/dist/utils/validation.js +38 -0
- package/package.json +1 -4
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
## Functions
|
|
2
|
+
|
|
3
|
+
### `writeLlmsTxt`
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
async function writeLlmsTxt(docs: GeneratedDoc[], outputDir: string, options: { projectName?: string; description?: string } = {}): Promise<void>
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Use this to generate an `llms.txt` file for your API documentation, making it discoverable and consumable by LLM-powered answer engines following the [llmstxt.org](https://llmstxt.org) convention.
|
|
10
|
+
|
|
11
|
+
This is particularly useful for Answer Engine Optimization (AEO) — the equivalent of SEO but for AI assistants and LLM-based search tools.
|
|
12
|
+
|
|
13
|
+
#### Parameters
|
|
14
|
+
|
|
15
|
+
| Name | Type | Required | Description |
|
|
16
|
+
|------|------|----------|-------------|
|
|
17
|
+
| `docs` | `GeneratedDoc[]` | Yes | Array of generated documentation objects to include in the output file |
|
|
18
|
+
| `outputDir` | `string` | Yes | Directory path where the `llms.txt` file will be written |
|
|
19
|
+
| `options` | `object` | No | Optional configuration for the output file |
|
|
20
|
+
| `options.projectName` | `string` | No | Name of the project/API (defaults to `'API'`) |
|
|
21
|
+
| `options.description` | `string` | No | Short description of the project included in the file header |
|
|
22
|
+
|
|
23
|
+
#### Returns
|
|
24
|
+
|
|
25
|
+
`Promise<void>` — Resolves when the `llms.txt` file has been successfully written to `outputDir`. Rejects if the directory cannot be created or the file cannot be written.
|
|
26
|
+
|
|
27
|
+
## Output Format
|
|
28
|
+
|
|
29
|
+
The generated `llms.txt` file follows the [llmstxt.org](https://llmstxt.org) specification, which structures documentation in a way that LLMs can efficiently parse and reference when answering questions about your API.
|
|
30
|
+
|
|
31
|
+
**Example:**
|
|
32
|
+
|
|
33
|
+
```typescript example.ts
|
|
34
|
+
import { mkdir, writeFile } from 'fs/promises'
|
|
35
|
+
import { join } from 'path'
|
|
36
|
+
import * as os from 'os'
|
|
37
|
+
import * as path from 'path'
|
|
38
|
+
|
|
39
|
+
// --- Inline types (mirrors the real GeneratedDoc shape) ---
|
|
40
|
+
type GeneratedDoc = {
|
|
41
|
+
filePath: string
|
|
42
|
+
elementName: string
|
|
43
|
+
content: string
|
|
44
|
+
description?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// --- Self-contained implementation of writeLlmsTxt ---
|
|
48
|
+
async function writeLlmsTxt(
|
|
49
|
+
docs: GeneratedDoc[],
|
|
50
|
+
outputDir: string,
|
|
51
|
+
options: { projectName?: string; description?: string } = {}
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
const projectName = options.projectName || 'API'
|
|
54
|
+
const description = options.description || ''
|
|
55
|
+
|
|
56
|
+
const lines: string[] = []
|
|
57
|
+
|
|
58
|
+
// Header block following llmstxt.org convention
|
|
59
|
+
lines.push(`# ${projectName}`)
|
|
60
|
+
if (description) {
|
|
61
|
+
lines.push('')
|
|
62
|
+
lines.push(`> ${description}`)
|
|
63
|
+
}
|
|
64
|
+
lines.push('')
|
|
65
|
+
lines.push('## Documentation')
|
|
66
|
+
lines.push('')
|
|
67
|
+
|
|
68
|
+
// List each documented element
|
|
69
|
+
for (const doc of docs) {
|
|
70
|
+
const label = doc.elementName || path.basename(doc.filePath)
|
|
71
|
+
lines.push(`- **${label}**: ${doc.description || doc.content.slice(0, 120).replace(/\n/g, ' ')}`)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
lines.push('')
|
|
75
|
+
lines.push(`---`)
|
|
76
|
+
lines.push(`Generated by autodocs • ${new Date().toISOString()}`)
|
|
77
|
+
|
|
78
|
+
const outputPath = join(outputDir, 'llms.txt')
|
|
79
|
+
|
|
80
|
+
await mkdir(outputDir, { recursive: true })
|
|
81
|
+
await writeFile(outputPath, lines.join('\n'), 'utf-8')
|
|
82
|
+
|
|
83
|
+
console.log(`✅ llms.txt written to: ${outputPath}`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// --- Example usage ---
|
|
87
|
+
const exampleDocs: GeneratedDoc[] = [
|
|
88
|
+
{
|
|
89
|
+
filePath: 'src/auth/login.ts',
|
|
90
|
+
elementName: 'login',
|
|
91
|
+
description: 'Authenticates a user and returns a session token.',
|
|
92
|
+
content: 'async function login(email: string, password: string): Promise<string>',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
filePath: 'src/auth/logout.ts',
|
|
96
|
+
elementName: 'logout',
|
|
97
|
+
description: 'Invalidates the current session token.',
|
|
98
|
+
content: 'async function logout(token: string): Promise<void>',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
filePath: 'src/users/getUser.ts',
|
|
102
|
+
elementName: 'getUser',
|
|
103
|
+
description: 'Fetches a user record by their unique ID.',
|
|
104
|
+
content: 'async function getUser(userId: string): Promise<User>',
|
|
105
|
+
},
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
async function main() {
|
|
109
|
+
// Write to a temp directory so this example is safe to run anywhere
|
|
110
|
+
const outputDir = join(os.tmpdir(), 'autodocs-example')
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
await writeLlmsTxt(exampleDocs, outputDir, {
|
|
114
|
+
projectName: 'MyApp API',
|
|
115
|
+
description: 'REST and SDK documentation for the MyApp platform.',
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Verify the output
|
|
119
|
+
const { readFile } = await import('fs/promises')
|
|
120
|
+
const content = await readFile(join(outputDir, 'llms.txt'), 'utf-8')
|
|
121
|
+
console.log('\n--- Generated llms.txt ---\n')
|
|
122
|
+
console.log(content)
|
|
123
|
+
// Expected output:
|
|
124
|
+
// # MyApp API
|
|
125
|
+
//
|
|
126
|
+
// > REST and SDK documentation for the MyApp platform.
|
|
127
|
+
//
|
|
128
|
+
// ## Documentation
|
|
129
|
+
//
|
|
130
|
+
// - **login**: Authenticates a user and returns a session token.
|
|
131
|
+
// - **logout**: Invalidates the current session token.
|
|
132
|
+
// - **getUser**: Fetches a user record by their unique ID.
|
|
133
|
+
//
|
|
134
|
+
// ---
|
|
135
|
+
// Generated by autodocs • 2024-01-15T10:30:00.000Z
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('❌ Failed to write llms.txt:', error)
|
|
138
|
+
process.exit(1)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
main()
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `writeDocsToDirectory`
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
async function writeDocsToDirectory(results: FileGenerationResult[], outputDir: string, sourceDir: string): Promise<{ filesWritten: number; totalDocs: number }>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Use this to persist generated documentation to disk, writing markdown files into an output directory while preserving the relative structure of your source directory.
|
|
152
|
+
|
|
153
|
+
After generating docs for your source files, pass the results to this function to write them out as organized markdown files. It handles directory creation automatically and maps each source file to a corresponding output path.
|
|
154
|
+
|
|
155
|
+
### Parameters
|
|
156
|
+
|
|
157
|
+
| Name | Type | Required | Description |
|
|
158
|
+
|------|------|----------|-------------|
|
|
159
|
+
| `results` | `FileGenerationResult[]` | ✅ | Array of generation results, each containing a source file path and its generated documentation entries |
|
|
160
|
+
| `outputDir` | `string` | ✅ | Root directory where documentation files will be written (created if it doesn't exist) |
|
|
161
|
+
| `sourceDir` | `string` | ✅ | Root directory of your source files — used to compute relative paths so the output structure mirrors your source layout |
|
|
162
|
+
|
|
163
|
+
#### Returns
|
|
164
|
+
|
|
165
|
+
Returns a `Promise` that resolves to an object with:
|
|
166
|
+
|
|
167
|
+
| Field | Type | Description |
|
|
168
|
+
|-------|------|-------------|
|
|
169
|
+
| `filesWritten` | `number` | Number of markdown files successfully written to disk |
|
|
170
|
+
| `totalDocs` | `number` | Total number of individual documentation entries across all files |
|
|
171
|
+
|
|
172
|
+
### Notes
|
|
173
|
+
|
|
174
|
+
- Output directories are created recursively — no need to pre-create nested folders
|
|
175
|
+
- Source file paths are made relative to `sourceDir` to determine output paths, so `/src/utils/math.ts` becomes `<outputDir>/utils/math.md`
|
|
176
|
+
- Files with no generated docs are skipped and won't count toward `filesWritten`
|
|
177
|
+
|
|
178
|
+
**Example:**
|
|
179
|
+
|
|
180
|
+
```typescript example.ts
|
|
181
|
+
import { mkdir, writeFile } from 'fs/promises'
|
|
182
|
+
import { join, dirname, relative, basename } from 'path'
|
|
183
|
+
|
|
184
|
+
// --- Inline types (mirrors autodocs internals) ---
|
|
185
|
+
interface GeneratedDoc {
|
|
186
|
+
name: string
|
|
187
|
+
kind: 'function' | 'class' | 'interface' | 'type'
|
|
188
|
+
markdown: string
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
interface FileGenerationResult {
|
|
192
|
+
sourceFile: string
|
|
193
|
+
docs: GeneratedDoc[]
|
|
194
|
+
error?: string
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// --- Inline implementation of writeDocsToDirectory ---
|
|
198
|
+
async function writeDocsToDirectory(
|
|
199
|
+
results: FileGenerationResult[],
|
|
200
|
+
outputDir: string,
|
|
201
|
+
sourceDir: string
|
|
202
|
+
): Promise<{ filesWritten: number; totalDocs: number }> {
|
|
203
|
+
let filesWritten = 0
|
|
204
|
+
let totalDocs = 0
|
|
205
|
+
|
|
206
|
+
for (const result of results) {
|
|
207
|
+
if (result.error || result.docs.length === 0) continue
|
|
208
|
+
|
|
209
|
+
// Mirror source structure in output directory
|
|
210
|
+
const relativePath = relative(sourceDir, result.sourceFile)
|
|
211
|
+
const outputFile = join(
|
|
212
|
+
outputDir,
|
|
213
|
+
relativePath.replace(/\.(ts|tsx|js|jsx)$/, '.md')
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
// Combine all docs for this file into one markdown file
|
|
217
|
+
const content = result.docs
|
|
218
|
+
.map((doc) => `## ${doc.name}\n\n${doc.markdown}`)
|
|
219
|
+
.join('\n\n---\n\n')
|
|
220
|
+
|
|
221
|
+
await mkdir(dirname(outputFile), { recursive: true })
|
|
222
|
+
await writeFile(outputFile, content, 'utf-8')
|
|
223
|
+
|
|
224
|
+
filesWritten++
|
|
225
|
+
totalDocs += result.docs.length
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { filesWritten, totalDocs }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// --- Realistic usage example ---
|
|
232
|
+
const mockResults: FileGenerationResult[] = [
|
|
233
|
+
{
|
|
234
|
+
sourceFile: '/project/src/utils/math.ts',
|
|
235
|
+
docs: [
|
|
236
|
+
{
|
|
237
|
+
name: 'add',
|
|
238
|
+
kind: 'function',
|
|
239
|
+
markdown: 'Adds two numbers together.\n\n**Params:** `a: number`, `b: number`\n\n**Returns:** `number`',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'multiply',
|
|
243
|
+
kind: 'function',
|
|
244
|
+
markdown: 'Multiplies two numbers.\n\n**Params:** `a: number`, `b: number`\n\n**Returns:** `number`',
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
sourceFile: '/project/src/api/client.ts',
|
|
250
|
+
docs: [
|
|
251
|
+
{
|
|
252
|
+
name: 'ApiClient',
|
|
253
|
+
kind: 'class',
|
|
254
|
+
markdown: 'HTTP client for interacting with the REST API.\n\n**Constructor:** `new ApiClient(baseUrl: string)`',
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
// Simulates a file that failed to generate — should be skipped
|
|
260
|
+
sourceFile: '/project/src/broken/module.ts',
|
|
261
|
+
docs: [],
|
|
262
|
+
error: 'Parse error: unexpected token',
|
|
263
|
+
},
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
async function main() {
|
|
267
|
+
const outputDir = process.env.DOCS_OUTPUT_DIR || './docs-output'
|
|
268
|
+
const sourceDir = '/project/src'
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const { filesWritten, totalDocs } = await writeDocsToDirectory(
|
|
272
|
+
mockResults,
|
|
273
|
+
outputDir,
|
|
274
|
+
sourceDir
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
console.log(`✅ Documentation written successfully`)
|
|
278
|
+
console.log(` Files written : ${filesWritten}`) // Files written : 2
|
|
279
|
+
console.log(` Total docs : ${totalDocs}`) // Total docs : 3
|
|
280
|
+
// Output files:
|
|
281
|
+
// ./docs-output/utils/math.md (2 docs: add, multiply)
|
|
282
|
+
// ./docs-output/api/client.md (1 doc: ApiClient)
|
|
283
|
+
// ./docs-output/broken/module.md ← skipped (error + no docs)
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error('Failed to write docs:', error)
|
|
286
|
+
process.exit(1)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
main()
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### `groupDocsByFile`
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
function groupDocsByFile(docs: GeneratedDoc[]): FileGenerationResult[]
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Use this to organize a flat list of generated documentation objects into groups by their source file, making it easy to write one output file per source file.
|
|
300
|
+
|
|
301
|
+
This is the key step between generating individual doc entries and writing them to disk — it collapses a flat array into a file-keyed structure so you can iterate over files and write each one independently.
|
|
302
|
+
|
|
303
|
+
#### Parameters
|
|
304
|
+
|
|
305
|
+
| Name | Type | Required | Description |
|
|
306
|
+
|------|------|----------|-------------|
|
|
307
|
+
| `docs` | `GeneratedDoc[]` | ✅ | Flat array of generated documentation objects, each containing an `element` with a `filePath` property |
|
|
308
|
+
|
|
309
|
+
#### Returns
|
|
310
|
+
|
|
311
|
+
Returns `FileGenerationResult[]` — an array of objects where each entry represents one source file and contains all the `GeneratedDoc` entries that belong to it.
|
|
312
|
+
|
|
313
|
+
| Scenario | Result |
|
|
314
|
+
|----------|--------|
|
|
315
|
+
| Multiple docs from the same file | Grouped into a single `FileGenerationResult` |
|
|
316
|
+
| Docs from different files | Each file gets its own `FileGenerationResult` |
|
|
317
|
+
| Empty array passed | Returns an empty array `[]` |
|
|
318
|
+
|
|
319
|
+
**Example:**
|
|
320
|
+
|
|
321
|
+
```typescript example.ts
|
|
322
|
+
// ── Inline types (do not import from autodocs) ──────────────────────────────
|
|
323
|
+
|
|
324
|
+
interface CodeElement {
|
|
325
|
+
name: string
|
|
326
|
+
filePath: string
|
|
327
|
+
kind: 'function' | 'class' | 'interface' | 'type'
|
|
328
|
+
signature: string
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
interface GeneratedDoc {
|
|
332
|
+
element: CodeElement
|
|
333
|
+
markdown: string
|
|
334
|
+
generatedAt: Date
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
interface FileGenerationResult {
|
|
338
|
+
filePath: string
|
|
339
|
+
docs: GeneratedDoc[]
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── Inline implementation ────────────────────────────────────────────────────
|
|
343
|
+
|
|
344
|
+
function groupDocsByFile(docs: GeneratedDoc[]): FileGenerationResult[] {
|
|
345
|
+
const byFile = new Map<string, GeneratedDoc[]>()
|
|
346
|
+
|
|
347
|
+
for (const doc of docs) {
|
|
348
|
+
const file = doc.element.filePath
|
|
349
|
+
if (!byFile.has(file)) {
|
|
350
|
+
byFile.set(file, [])
|
|
351
|
+
}
|
|
352
|
+
byFile.get(file)!.push(doc)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return Array.from(byFile.entries()).map(([filePath, docs]) => ({
|
|
356
|
+
filePath,
|
|
357
|
+
docs,
|
|
358
|
+
}))
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ── Realistic usage example ──────────────────────────────────────────────────
|
|
362
|
+
|
|
363
|
+
const generatedDocs: GeneratedDoc[] = [
|
|
364
|
+
{
|
|
365
|
+
element: {
|
|
366
|
+
name: 'fetchUser',
|
|
367
|
+
filePath: 'src/api/users.ts',
|
|
368
|
+
kind: 'function',
|
|
369
|
+
signature: 'function fetchUser(id: string): Promise<User>',
|
|
370
|
+
},
|
|
371
|
+
markdown: '## fetchUser\n\nFetches a user by ID.',
|
|
372
|
+
generatedAt: new Date('2024-01-15T10:00:00Z'),
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
element: {
|
|
376
|
+
name: 'createUser',
|
|
377
|
+
filePath: 'src/api/users.ts', // same file as fetchUser
|
|
378
|
+
kind: 'function',
|
|
379
|
+
signature: 'function createUser(data: UserInput): Promise<User>',
|
|
380
|
+
},
|
|
381
|
+
markdown: '## createUser\n\nCreates a new user.',
|
|
382
|
+
generatedAt: new Date('2024-01-15T10:00:01Z'),
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
element: {
|
|
386
|
+
name: 'AuthService',
|
|
387
|
+
filePath: 'src/services/auth.ts', // different file
|
|
388
|
+
kind: 'class',
|
|
389
|
+
signature: 'class AuthService',
|
|
390
|
+
},
|
|
391
|
+
markdown: '## AuthService\n\nHandles authentication.',
|
|
392
|
+
generatedAt: new Date('2024-01-15T10:00:02Z'),
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
element: {
|
|
396
|
+
name: 'UserRole',
|
|
397
|
+
filePath: 'src/types/roles.ts', // another file
|
|
398
|
+
kind: 'type',
|
|
399
|
+
signature: "type UserRole = 'admin' | 'editor' | 'viewer'",
|
|
400
|
+
},
|
|
401
|
+
markdown: '## UserRole\n\nDefines available user roles.',
|
|
402
|
+
generatedAt: new Date('2024-01-15T10:00:03Z'),
|
|
403
|
+
},
|
|
404
|
+
]
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
const fileGroups = groupDocsByFile(generatedDocs)
|
|
408
|
+
|
|
409
|
+
console.log(`Grouped ${generatedDocs.length} docs into ${fileGroups.length} files:\n`)
|
|
410
|
+
|
|
411
|
+
for (const group of fileGroups) {
|
|
412
|
+
const names = group.docs.map(d => d.element.name).join(', ')
|
|
413
|
+
console.log(` 📄 ${group.filePath}`)
|
|
414
|
+
console.log(` ${group.docs.length} export(s): ${names}\n`)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Expected output:
|
|
418
|
+
// Grouped 4 docs into 3 files:
|
|
419
|
+
//
|
|
420
|
+
// 📄 src/api/users.ts
|
|
421
|
+
// 2 export(s): fetchUser, createUser
|
|
422
|
+
//
|
|
423
|
+
// 📄 src/services/auth.ts
|
|
424
|
+
// 1 export(s): AuthService
|
|
425
|
+
//
|
|
426
|
+
// 📄 src/types/roles.ts
|
|
427
|
+
// 1 export(s): UserRole
|
|
428
|
+
|
|
429
|
+
// Show how you'd use this to write files
|
|
430
|
+
console.log('--- Ready to write output files ---')
|
|
431
|
+
for (const group of fileGroups) {
|
|
432
|
+
const combinedMarkdown = group.docs.map(d => d.markdown).join('\n\n---\n\n')
|
|
433
|
+
console.log(`Would write ${combinedMarkdown.length} chars to docs for: ${group.filePath}`)
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error('Failed to group docs:', error)
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### `writeDocsByTopic`
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
async function writeDocsByTopic(docs: GeneratedDoc[], outputDir: string): Promise<{ filesWritten: number; totalDocs: number; topics: Topic[] }>
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Use this to write generated documentation files to disk, organized into topic-based subdirectories — ideal for creating structured doc sites where related functions and classes are grouped together.
|
|
447
|
+
|
|
448
|
+
Instead of dumping all docs into a flat directory, `writeDocsByTopic` automatically clusters your `GeneratedDoc` entries by topic and writes each one into the appropriate subfolder, returning a summary of what was written.
|
|
449
|
+
|
|
450
|
+
#### Parameters
|
|
451
|
+
|
|
452
|
+
| Name | Type | Required | Description |
|
|
453
|
+
|------|------|----------|-------------|
|
|
454
|
+
| `docs` | `GeneratedDoc[]` | ✅ | Array of generated documentation objects to organize and write |
|
|
455
|
+
| `outputDir` | `string` | ✅ | Root directory where topic-organized subdirectories and files will be created |
|
|
456
|
+
|
|
457
|
+
#### Returns
|
|
458
|
+
|
|
459
|
+
Returns a `Promise` that resolves to an object with:
|
|
460
|
+
|
|
461
|
+
| Field | Type | Description |
|
|
462
|
+
|-------|------|-------------|
|
|
463
|
+
| `filesWritten` | `number` | Number of markdown files successfully written to disk |
|
|
464
|
+
| `totalDocs` | `number` | Total number of docs processed (may differ from `filesWritten` if some were skipped) |
|
|
465
|
+
| `topics` | `Topic[]` | Array of topic objects describing how docs were grouped, useful for generating a table of contents |
|
|
466
|
+
|
|
467
|
+
## Notes
|
|
468
|
+
|
|
469
|
+
- Output directories are created automatically if they don't exist
|
|
470
|
+
- Each `Topic` in the returned array contains the topic name and the docs assigned to it
|
|
471
|
+
- Docs without an explicit topic may be grouped under a default/uncategorized topic
|
|
472
|
+
- Useful as a final step in a doc generation pipeline after parsing and formatting
|
|
473
|
+
|
|
474
|
+
**Example:**
|
|
475
|
+
|
|
476
|
+
```typescript example.ts
|
|
477
|
+
import fs from 'fs/promises'
|
|
478
|
+
import path from 'path'
|
|
479
|
+
import os from 'os'
|
|
480
|
+
|
|
481
|
+
// ── Inline types (mirrors autodocs internals) ────────────────────────────────
|
|
482
|
+
|
|
483
|
+
type GeneratedDoc = {
|
|
484
|
+
elementName: string
|
|
485
|
+
topic: string
|
|
486
|
+
filePath: string
|
|
487
|
+
markdown: string
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
type Topic = {
|
|
491
|
+
name: string
|
|
492
|
+
slug: string
|
|
493
|
+
docs: GeneratedDoc[]
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// ── Inline implementation (mirrors autodocs behaviour) ───────────────────────
|
|
497
|
+
|
|
498
|
+
function organizeByTopic(docs: GeneratedDoc[]): Topic[] {
|
|
499
|
+
const map = new Map<string, GeneratedDoc[]>()
|
|
500
|
+
for (const doc of docs) {
|
|
501
|
+
const key = doc.topic || 'general'
|
|
502
|
+
if (!map.has(key)) map.set(key, [])
|
|
503
|
+
map.get(key)!.push(doc)
|
|
504
|
+
}
|
|
505
|
+
return Array.from(map.entries()).map(([name, topicDocs]) => ({
|
|
506
|
+
name,
|
|
507
|
+
slug: name.toLowerCase().replace(/\s+/g, '-'),
|
|
508
|
+
docs: topicDocs,
|
|
509
|
+
}))
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function writeDocsByTopic(
|
|
513
|
+
docs: GeneratedDoc[],
|
|
514
|
+
outputDir: string
|
|
515
|
+
): Promise<{ filesWritten: number; totalDocs: number; topics: Topic[] }> {
|
|
516
|
+
let filesWritten = 0
|
|
517
|
+
const topics = organizeByTopic(docs)
|
|
518
|
+
|
|
519
|
+
for (const topic of topics) {
|
|
520
|
+
const topicDir = path.join(outputDir, topic.slug)
|
|
521
|
+
await fs.mkdir(topicDir, { recursive: true })
|
|
522
|
+
|
|
523
|
+
for (const doc of topic.docs) {
|
|
524
|
+
const fileName = `${doc.elementName.toLowerCase().replace(/\s+/g, '-')}.md`
|
|
525
|
+
const filePath = path.join(topicDir, fileName)
|
|
526
|
+
await fs.writeFile(filePath, doc.markdown, 'utf8')
|
|
527
|
+
filesWritten++
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return { filesWritten, totalDocs: docs.length, topics }
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// ── Example usage ─────────────────────────────────────────────────────────────
|
|
535
|
+
|
|
536
|
+
const sampleDocs: GeneratedDoc[] = [
|
|
537
|
+
{
|
|
538
|
+
elementName: 'fetchUser',
|
|
539
|
+
topic: 'API',
|
|
540
|
+
filePath: 'src/api/fetchUser.ts',
|
|
541
|
+
markdown: '# fetchUser\n\nFetches a user by ID from the REST API.\n',
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
elementName: 'createUser',
|
|
545
|
+
topic: 'API',
|
|
546
|
+
filePath: 'src/api/createUser.ts',
|
|
547
|
+
markdown: '# createUser\n\nCreates a new user record.\n',
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
elementName: 'hashPassword',
|
|
551
|
+
topic: 'Auth',
|
|
552
|
+
filePath: 'src/auth/hashPassword.ts',
|
|
553
|
+
markdown: '# hashPassword\n\nHashes a plaintext password using bcrypt.\n',
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
elementName: 'formatDate',
|
|
557
|
+
topic: 'Utils',
|
|
558
|
+
filePath: 'src/utils/formatDate.ts',
|
|
559
|
+
markdown: '# formatDate\n\nFormats a Date object into a locale string.\n',
|
|
560
|
+
},
|
|
561
|
+
]
|
|
562
|
+
|
|
563
|
+
async function main() {
|
|
564
|
+
// Write to a temp directory so the example is safe to run anywhere
|
|
565
|
+
const outputDir = path.join(os.tmpdir(), 'autodocs-example-output')
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
console.log(`Writing docs to: ${outputDir}`)
|
|
569
|
+
|
|
570
|
+
const result = await writeDocsByTopic(sampleDocs, outputDir)
|
|
571
|
+
|
|
572
|
+
console.log('\n✅ Done!')
|
|
573
|
+
console.log(` Files written : ${result.filesWritten}`)
|
|
574
|
+
console.log(` Total docs : ${result.totalDocs}`)
|
|
575
|
+
console.log(` Topics found : ${result.topics.map(t => t.name).join(', ')}`)
|
|
576
|
+
|
|
577
|
+
// Show the directory tree that was created
|
|
578
|
+
console.log('\n📁 Output structure:')
|
|
579
|
+
for (const topic of result.topics) {
|
|
580
|
+
console.log(` ${outputDir}/${topic.slug}/`)
|
|
581
|
+
for (const doc of topic.docs) {
|
|
582
|
+
console.log(` └─ ${doc.elementName.toLowerCase()}.md`)
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Expected output:
|
|
587
|
+
// ✅ Done!
|
|
588
|
+
// Files written : 4
|
|
589
|
+
// Total docs : 4
|
|
590
|
+
// Topics found : API, Auth, Utils
|
|
591
|
+
//
|
|
592
|
+
// 📁 Output structure:
|
|
593
|
+
// /tmp/autodocs-example-output/api/
|
|
594
|
+
// └─ fetchuser.md
|
|
595
|
+
// └─ createuser.md
|
|
596
|
+
// /tmp/autodocs-example-output/auth/
|
|
597
|
+
// └─ hashpassword.md
|
|
598
|
+
// /tmp/autodocs-example-output/utils/
|
|
599
|
+
// └─ formatdate.md
|
|
600
|
+
|
|
601
|
+
} catch (error) {
|
|
602
|
+
if (error instanceof Error) {
|
|
603
|
+
console.error('Failed to write docs:', error.message)
|
|
604
|
+
} else {
|
|
605
|
+
console.error('Unexpected error:', error)
|
|
606
|
+
}
|
|
607
|
+
process.exit(1)
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
main()
|
|
612
|
+
```
|
|
613
|
+
|