@gunshi/docs 0.27.0-beta.4

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.
@@ -0,0 +1,594 @@
1
+ # Documentation Generation
2
+
3
+ Gunshi provides a powerful feature for automatically generating documentation for your CLI applications.
4
+
5
+ This guide explains how to use the `generate` function to generate documentation programmatically.
6
+
7
+ ## Using the `generate` Function
8
+
9
+ The following example demonstrates how to use the `generate` function to programmatically capture usage information for a command and save it to a documentation file:
10
+
11
+ ```ts [cli.ts]
12
+ import { define } from 'gunshi'
13
+ import { generate } from 'gunshi/generator'
14
+ import { promises as fs } from 'node:fs'
15
+
16
+ import type { Command } from 'gunshi'
17
+
18
+ // Define your command
19
+ const command = define({
20
+ name: 'my-command',
21
+ description: 'A sample command',
22
+ args: {
23
+ input: {
24
+ type: 'string',
25
+ short: 'i',
26
+ description: 'Input file'
27
+ },
28
+ output: {
29
+ type: 'string',
30
+ short: 'o',
31
+ description: 'Output file'
32
+ }
33
+ }
34
+ // ... and define `run`
35
+ })
36
+
37
+ // Generate documentation
38
+ async function main() {
39
+ // Generate the usage information
40
+ const usageText: string = await generate(null, command, {
41
+ name: 'my-cli',
42
+ version: '1.0.0',
43
+ description: 'My CLI tool'
44
+ })
45
+
46
+ // Now you can use the usage text to generate documentation
47
+ await fs.writeFile('docs/cli-usage.md', `# CLI Usage\n\n\`\`\`sh\n${usageText}\n\`\`\``, 'utf8')
48
+
49
+ console.log('Documentation generated successfully!')
50
+ }
51
+
52
+ // Generate!
53
+ await main()
54
+ ```
55
+
56
+ <!-- eslint-disable markdown/no-missing-label-refs -->
57
+
58
+ > [!TIP]
59
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/advanced/docs-gen/basic).
60
+
61
+ <!-- eslint-enable markdown/no-missing-label-refs -->
62
+
63
+ The `generate` function programmatically captures usage information for your CLI commands and returns it as a string.
64
+
65
+ This allows you to generate documentation files, create API documentation, or integrate usage information into your build process.
66
+
67
+ Unlike the `cli` function which executes commands and prints output to the console, `generate` is designed specifically for documentation generation:
68
+
69
+ - **Silent Mode**: Internally sets `usageSilent: true` to capture output as a string rather than printing to console. When this flag is set, the internal `ctx.log` function is replaced with a no-op function, preventing console output while still returning the generated usage text
70
+ - **No Execution**: Only generates usage text without running command logic
71
+ - **Return Value**: Returns the generated usage text as a string (or empty string if generation fails)
72
+
73
+ The `generate` function takes three parameters:
74
+
75
+ - `command` (string | null): The sub-command name to generate documentation for. Pass `null` when generating documentation for the entry command or when you don't have sub-commands
76
+ - `entry` (Command | LazyCommand): The command object containing the command definition, or a lazy command that will be loaded to get the command
77
+ - `opts` (CliOptions): Optional configuration including:
78
+ - `name`: The CLI program name
79
+ - `version`: Version string to display
80
+ - `description`: Program description
81
+ - `subCommands`: Map of sub-commands (if applicable)
82
+ - `renderHeader`, `renderUsage`: Custom renderer functions
83
+ - **Note**: The `usageSilent` option is automatically set to `true` internally, so any value you provide will be overridden
84
+
85
+ Returns a Promise that resolves to the generated usage text as a string.
86
+
87
+ If no usage is generated (e.g., when renderUsage is not defined), returns an empty string.
88
+
89
+ ## Generating Documentation for Multiple Commands
90
+
91
+ When your CLI has sub-commands, you can iterate through them to generate comprehensive documentation.
92
+
93
+ Here's how to generate documentation for each command separately:
94
+
95
+ ```ts [cli.ts]
96
+ import { define } from 'gunshi'
97
+ import { generate } from 'gunshi/generator'
98
+ import { promises as fs } from 'node:fs'
99
+
100
+ import type { Command, CliOptions } from 'gunshi'
101
+
102
+ // Define your commands
103
+ const createCommand = define({
104
+ name: 'create',
105
+ description: 'Create a new resource',
106
+ args: {
107
+ name: {
108
+ type: 'string',
109
+ short: 'n',
110
+ required: true,
111
+ description: 'Name of the resource'
112
+ }
113
+ }
114
+ // ... and define `run`
115
+ })
116
+
117
+ const listCommand = define({
118
+ name: 'list',
119
+ description: 'List all resources',
120
+ args: {
121
+ format: {
122
+ type: 'string',
123
+ short: 'f',
124
+ description: 'Output format (json, table)'
125
+ }
126
+ }
127
+ // ... and define `run`
128
+ })
129
+
130
+ // Create a Map of sub-commands
131
+ const subCommands = {
132
+ create: createCommand,
133
+ list: listCommand
134
+ }
135
+
136
+ // Define the main command
137
+ const mainCommand = define({
138
+ name: 'manage',
139
+ description: 'Manage resources'
140
+ // ... and define `run`
141
+ })
142
+
143
+ // Generate documentation for all commands
144
+ async function main() {
145
+ const cliOptions: CliOptions = {
146
+ name: 'my-cli',
147
+ version: '1.0.0',
148
+ description: 'My CLI tool',
149
+ subCommands
150
+ }
151
+
152
+ // Generate main help
153
+ const mainUsage = await generate(null, mainCommand, cliOptions)
154
+ await fs.writeFile('docs/cli-main.md', `# CLI Usage\n\n\`\`\`sh\n${mainUsage}\n\`\`\``, 'utf8')
155
+
156
+ // Generate help for each sub-command
157
+ for (const name of Object.keys(subCommands)) {
158
+ const commandUsage = await generate(name, mainCommand, cliOptions)
159
+ await fs.writeFile(
160
+ `docs/cli-${name}.md`,
161
+ `# ${name.charAt(0).toUpperCase() + name.slice(1)} Command\n\n\`\`\`sh\n${commandUsage}\n\`\`\``,
162
+ 'utf8'
163
+ )
164
+ }
165
+
166
+ console.log('All documentation generated successfully!')
167
+ }
168
+
169
+ // Generate!
170
+ await main()
171
+ ```
172
+
173
+ <!-- eslint-disable markdown/no-missing-label-refs -->
174
+
175
+ > [!TIP]
176
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/advanced/docs-gen/sub-command).
177
+
178
+ <!-- eslint-enable markdown/no-missing-label-refs -->
179
+
180
+ ## Creating Rich Documentation
181
+
182
+ You can combine the generated usage information with additional content to create rich documentation:
183
+
184
+ ```ts
185
+ import { define } from 'gunshi'
186
+ import { generate } from 'gunshi/generator'
187
+ import { promises as fs } from 'node:fs'
188
+
189
+ // Generate rich documentation
190
+ async function main() {
191
+ const command = define({
192
+ name: 'data-processor',
193
+ description: 'Process data files',
194
+ args: {
195
+ input: {
196
+ type: 'string',
197
+ short: 'i',
198
+ required: true,
199
+ description: 'Input file path'
200
+ },
201
+ format: {
202
+ type: 'string',
203
+ short: 'f',
204
+ description: 'Output format (json, csv, xml)'
205
+ },
206
+ output: {
207
+ type: 'string',
208
+ short: 'o',
209
+ description: 'Output file path'
210
+ }
211
+ }
212
+ // ... and define `run`
213
+ })
214
+
215
+ // Generate the usage information
216
+ const usageText = await generate(null, command, {
217
+ name: 'data-processor',
218
+ version: '1.0.0',
219
+ description: 'A data processing utility'
220
+ })
221
+
222
+ // Create rich documentation
223
+ const documentation = `
224
+ # Data Processor CLI
225
+
226
+ A command-line utility for processing data files in various formats.
227
+
228
+ ## Installation
229
+
230
+ \`\`\`sh
231
+ npm install -g data-processor
232
+ \`\`\`
233
+
234
+ ## Usage
235
+
236
+ \`\`\`sh
237
+ ${usageText}
238
+ \`\`\`
239
+
240
+ ## Examples
241
+
242
+ ### Convert a CSV file to JSON
243
+
244
+ \`\`\`sh
245
+ data-processor --input data.csv --format json --output data.json
246
+ \`\`\`
247
+
248
+ ### Process a file and print to stdout
249
+
250
+ \`\`\`sh
251
+ data-processor --input data.csv
252
+ \`\`\`
253
+
254
+ ## Advanced Usage
255
+
256
+ For more complex scenarios, you can:
257
+
258
+ 1. Chain commands with pipes
259
+ 2. Use glob patterns for batch processing
260
+ 3. Configure processing with a config file
261
+
262
+ ## API Reference
263
+
264
+ The CLI is built on top of the data-processor library, which you can also use programmatically.
265
+ `
266
+
267
+ await fs.writeFile('docs/data-processor.md', documentation, 'utf8')
268
+ console.log('Rich documentation generated successfully!')
269
+ }
270
+
271
+ // Generate!
272
+ await main()
273
+ ```
274
+
275
+ <!-- eslint-disable markdown/no-missing-label-refs -->
276
+
277
+ > [!TIP]
278
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/advanced/docs-gen/rich).
279
+
280
+ <!-- eslint-enable markdown/no-missing-label-refs -->
281
+
282
+ ## Automating Documentation Generation
283
+
284
+ You can automate documentation generation as part of your build process:
285
+
286
+ <!-- eslint-disable markdown/no-missing-label-refs -->
287
+
288
+ > [!NOTE]
289
+ > The following example uses Node.js-specific `__dirname` approach. For cross-runtime compatibility:
290
+ >
291
+ > - **Deno**: Use `import.meta.dirname` or `fromFileUrl(import.meta.url)`
292
+ > - **Bun**: Use `import.meta.dir` or Node.js-compatible approach
293
+
294
+ <!-- eslint-enable markdown/no-missing-label-refs -->
295
+
296
+ ```ts [scripts/generate-docs.ts]
297
+ import { generate } from 'gunshi/generator'
298
+ import { promises as fs } from 'node:fs'
299
+ import { fileURLToPath } from 'node:url'
300
+ import path from 'node:path'
301
+
302
+ import type { Command, CliOptions } from 'gunshi'
303
+
304
+ // Get the directory of the current module
305
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
306
+ const rootDir = path.resolve(__dirname, '..')
307
+ const docsDir = path.join(rootDir, 'docs')
308
+
309
+ // Import your commands
310
+ import { mainCommand, subCommands } from '../src/commands.ts'
311
+
312
+ async function main() {
313
+ const cliOptions: CliOptions = {
314
+ name: 'my-cli',
315
+ version: '1.0.0',
316
+ description: 'My CLI tool',
317
+ subCommands
318
+ }
319
+
320
+ // Generate main help
321
+ const mainUsage = await generate(null, mainCommand, cliOptions)
322
+
323
+ // Create the CLI reference page
324
+ const cliReference = `# CLI Reference
325
+
326
+ ## Main Command
327
+
328
+ \`\`\`sh
329
+ ${mainUsage}
330
+ \`\`\`
331
+
332
+ ## Sub-commands
333
+
334
+ `
335
+
336
+ // Add each sub-command
337
+ let fullReference = cliReference
338
+ for (const name of Object.keys(subCommands)) {
339
+ const commandUsage = await generate(name, mainCommand, cliOptions)
340
+ fullReference += `### ${name.charAt(0).toUpperCase() + name.slice(1)}
341
+
342
+ \`\`\`sh
343
+ ${commandUsage}
344
+ \`\`\`
345
+
346
+ `
347
+ }
348
+
349
+ // Write the documentation
350
+ await fs.writeFile(path.join(docsDir, 'cli-reference.md'), fullReference, 'utf8')
351
+ console.log('Documentation generated successfully!')
352
+ }
353
+
354
+ // Generate!
355
+ await main()
356
+ ```
357
+
358
+ Then add a script to your `package.json`:
359
+
360
+ ```json [package.json]
361
+ {
362
+ "scripts": {
363
+ "docs:generate": "tsx scripts/generate-docs.ts",
364
+ "docs:build": "npm run docs:generate && vitepress build docs"
365
+ }
366
+ }
367
+ ```
368
+
369
+ ## Generating Unix Man Pages
370
+
371
+ Unix man pages (short for "manual pages") are a traditional form of documentation for command-line tools on Unix-like operating systems.
372
+
373
+ You can use Gunshi's `generate` function to generate man pages for your CLI applications.
374
+
375
+ ### Introduction to Man Pages
376
+
377
+ Man pages are the standard documentation format for Unix and Unix-like systems.
378
+
379
+ They provide comprehensive documentation directly accessible from the command line using the `man` command.
380
+
381
+ Man pages follow a specific structure with numbered sections (1 for user commands, 2 for system calls, etc.) and standardized formatting conventions.
382
+
383
+ The most common format for man pages is roff (runoff), though modern tools allow you to write documentation in simpler formats like Markdown and convert them to roff.
384
+
385
+ ### Generating Man Pages with Gunshi
386
+
387
+ The following example demonstrates how to generate Unix man page documentation using Gunshi's `generate` function with a custom renderer.
388
+
389
+ The custom renderer (`renderManPageUsage`) creates markdown output that can be converted to the man page format (roff) using tools like `marked-man`.
390
+
391
+ The renderer function structures the output according to standard man page sections:
392
+
393
+ - **NAME**: Command name and brief description
394
+ - **SYNOPSIS**: Command syntax overview
395
+ - **DESCRIPTION**: Detailed command description
396
+ - **OPTIONS**: Available command-line options with descriptions
397
+ - **EXAMPLES**: Usage examples (references the command's help)
398
+ - **AUTHOR**: Author information
399
+ - **SEE ALSO**: Related documentation and resources
400
+
401
+ This approach allows you to maintain man pages alongside your CLI code, ensuring they stay synchronized:
402
+
403
+ ```ts [cli.ts]
404
+ import { define } from 'gunshi'
405
+ import { generate } from 'gunshi/generator'
406
+ import { execFileSync } from 'node:child_process'
407
+ import { promises as fs } from 'node:fs'
408
+ import path from 'node:path'
409
+
410
+ import type { CommandContext } from 'gunshi'
411
+
412
+ // Define custom usage renderer that outputs markdown convertible to man page format (roff) using marked-man
413
+ function renderManPageUsage(ctx: CommandContext) {
414
+ const lines: string[] = []
415
+
416
+ // NAME
417
+ lines.push(`# ${ctx.name}(1) -- ${ctx.description || 'CLI tool'}`, '')
418
+
419
+ // SYNOPSIS
420
+ lines.push('## SYNOPSIS')
421
+ lines.push(`${ctx.env.name} <OPTIONS>`, '')
422
+
423
+ // DESCRIPTION
424
+ lines.push('## DESCRIPTION')
425
+ lines.push(ctx.description || '', '')
426
+
427
+ // OPTIONS
428
+ lines.push('## OPTIONS')
429
+ for (const [name, schema] of Object.entries(ctx.args)) {
430
+ const options = [`\`--${name}\``]
431
+ if (schema.short) {
432
+ options.unshift(`\`-${schema.short}\``)
433
+ }
434
+ let value = ''
435
+ if (schema.type !== 'boolean') {
436
+ value = schema.default ? `[${name}]` : `<${name}>`
437
+ }
438
+ lines.push(`- ${options.join(', ')}${value ? ` ${value}` : ''}`)
439
+ lines.push(` ${schema.description || ''}`)
440
+ lines.push('')
441
+ }
442
+
443
+ // EXAMPLES
444
+ lines.push('## EXAMPLES')
445
+ lines.push('See command `--help` for examples', '')
446
+
447
+ // AUTHOR
448
+ lines.push('## AUTHOR')
449
+ lines.push('Created by yours', '')
450
+
451
+ // SEE ALSO
452
+ lines.push('## SEE ALSO')
453
+ lines.push('- man: `man my-tool`', '')
454
+ lines.push('- website: https://my-tools.com/references/cli', '')
455
+ lines.push('- repository: https://github.com/your-username/my-tool', '')
456
+
457
+ return Promise.resolve(lines.join('\n'))
458
+ }
459
+
460
+ async function main() {
461
+ const command = define({
462
+ name: 'my-tool',
463
+ description: 'A utility for processing data',
464
+ args: {
465
+ input: {
466
+ type: 'string',
467
+ short: 'i',
468
+ required: true,
469
+ description: 'Input file path'
470
+ },
471
+ output: {
472
+ type: 'string',
473
+ short: 'o',
474
+ description: 'Output file path (defaults to stdout)'
475
+ },
476
+ format: {
477
+ type: 'string',
478
+ short: 'f',
479
+ description: 'Output format (json, yaml, xml)'
480
+ },
481
+ verbose: {
482
+ type: 'boolean',
483
+ short: 'V',
484
+ description: 'Enable verbose output'
485
+ }
486
+ },
487
+ examples: `1. Process a file and output to stdout
488
+ $ my-tool --input data.csv
489
+
490
+ 2. Process a file and save to a specific format
491
+ $ my-tool --input data.csv --output result.yaml --format yaml
492
+
493
+ 3. Enable verbose output
494
+ $ my-tool --input data.csv --verbose`
495
+ // ... and define `run`
496
+ })
497
+
498
+ // Generate the usage with custom renderer
499
+ const usageText = await generate(null, command, {
500
+ name: 'my-tool',
501
+ version: '1.0.0',
502
+ description: 'A utility for processing data',
503
+ renderHeader: null, // no display header on console
504
+ renderUsage: renderManPageUsage // set custom usage renderer
505
+ })
506
+
507
+ // Prerequisites: Install marked-man for converting markdown to man page format
508
+ // npm install -g marked-man
509
+ // or add it to your project: npm install --save-dev marked-man
510
+
511
+ // Write the markdown file
512
+ const mdFile = path.join(process.cwd(), 'my-tool.1.md')
513
+ await fs.writeFile(mdFile, usageText, 'utf8')
514
+
515
+ // Convert markdown to man page format using marked-man
516
+ try {
517
+ execFileSync('marked-man', ['--input', mdFile, '--output', 'my-tool.1'])
518
+ console.log('Man page generated successfully: my-tool.1')
519
+ } catch (error) {
520
+ console.error('Error generating man page:', (error as Error).message)
521
+ console.log('Make sure marked-man is installed: npm install -g marked-man')
522
+ }
523
+ }
524
+
525
+ // Generate!
526
+ await main()
527
+ ```
528
+
529
+ <!-- eslint-disable markdown/no-missing-label-refs -->
530
+
531
+ > [!TIP]
532
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/advanced/docs-gen/man)
533
+
534
+ <!-- eslint-enable markdown/no-missing-label-refs -->
535
+
536
+ ### Installing Man Pages
537
+
538
+ Once you've generated a man page, you can install it on Unix-like systems:
539
+
540
+ 1. **Local installation** (for development):
541
+
542
+ ```sh
543
+ # Copy to your local man pages directory
544
+ cp my-tool.1 ~/.local/share/man/man1/
545
+ # Update the man database
546
+ mandb
547
+ ```
548
+
549
+ 2. **System-wide installation** (for packages):
550
+
551
+ ```sh
552
+ # Copy to the system man pages directory (requires sudo)
553
+ sudo cp my-tool.1 /usr/local/share/man/man1/
554
+ # Update the man database
555
+ sudo mandb
556
+ ```
557
+
558
+ 3. **Package installation** (for npm packages):
559
+ Add this to your `package.json`:
560
+ ```json [package.json]
561
+ {
562
+ "man": ["./man/my-tool.1"]
563
+ }
564
+ ```
565
+
566
+ ### Viewing Man Pages
567
+
568
+ After installation, users can view your man page using:
569
+
570
+ ```sh
571
+ man my-tool
572
+ ```
573
+
574
+ With the man page generation complete, let's look at broader guidelines for maintaining and generating documentation effectively.
575
+
576
+ ## Documentation Generation Guidelines
577
+
578
+ When working with Gunshi's documentation generation features, consider these important guidelines:
579
+
580
+ ### Keeping Documentation Current
581
+
582
+ Automating documentation generation as part of your build process ensures that your documentation stays synchronized with your code. This approach prevents documentation drift and maintains consistency across your project.
583
+
584
+ ### Enhancing Generated Content
585
+
586
+ The auto-generated usage information provides a solid foundation, but combining it with hand-written examples and detailed explanations creates more valuable documentation. Consider adding context-specific examples that demonstrate real-world use cases.
587
+
588
+ ### Leveraging Custom Renderers
589
+
590
+ For projects requiring specific documentation formats or styling, custom renderers provide fine-grained control over the output. See [Custom Rendering](./custom-rendering.md) for implementation details.
591
+
592
+ ### Documentation Testing
593
+
594
+ Including documentation tests in your test suite verifies that the generated documentation accurately reflects your CLI's behavior. This practice helps catch discrepancies between implementation and documentation early in the development process.