@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.
- package/LICENSE +20 -0
- package/package.json +52 -0
- package/src/guide/advanced/advanced-lazy-loading.md +312 -0
- package/src/guide/advanced/command-hooks.md +469 -0
- package/src/guide/advanced/context-extensions.md +545 -0
- package/src/guide/advanced/custom-rendering.md +945 -0
- package/src/guide/advanced/docs-gen.md +594 -0
- package/src/guide/advanced/internationalization.md +677 -0
- package/src/guide/advanced/type-system.md +561 -0
- package/src/guide/essentials/auto-usage.md +281 -0
- package/src/guide/essentials/composable.md +332 -0
- package/src/guide/essentials/declarative.md +724 -0
- package/src/guide/essentials/getting-started.md +252 -0
- package/src/guide/essentials/lazy-async.md +408 -0
- package/src/guide/essentials/plugin-system.md +472 -0
- package/src/guide/essentials/type-safe.md +154 -0
- package/src/guide/introduction/setup.md +68 -0
- package/src/guide/introduction/what-is-gunshi.md +68 -0
- package/src/guide/plugin/decorators.md +545 -0
- package/src/guide/plugin/dependencies.md +519 -0
- package/src/guide/plugin/extensions.md +317 -0
- package/src/guide/plugin/getting-started.md +298 -0
- package/src/guide/plugin/guidelines.md +940 -0
- package/src/guide/plugin/introduction.md +294 -0
- package/src/guide/plugin/lifecycle.md +432 -0
- package/src/guide/plugin/list.md +37 -0
- package/src/guide/plugin/testing.md +843 -0
- package/src/guide/plugin/type-system.md +529 -0
- package/src/index.md +44 -0
- package/src/release/v0.27.md +722 -0
- package/src/showcase.md +11 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
# Plugin System
|
|
2
|
+
|
|
3
|
+
## What's a Plugin?
|
|
4
|
+
|
|
5
|
+
A plugin in Gunshi is a modular extension that adds functionality to your CLI application without modifying its core code.
|
|
6
|
+
|
|
7
|
+
Think of plugins as building blocks that you can snap together to create powerful command-line tools.
|
|
8
|
+
|
|
9
|
+
### Why Use Plugins?
|
|
10
|
+
|
|
11
|
+
Plugins solve common CLI development challenges:
|
|
12
|
+
|
|
13
|
+
- **Separation of Concerns**: Plugins keep your command logic clean by handling cross-cutting functionality like logging, authentication, or database connections separately. Your commands focus on their primary task while plugins handle the supporting infrastructure.
|
|
14
|
+
- **Reusability**: Write functionality once, use it everywhere. A plugin created for one command can be reused across your entire CLI or even in different projects, saving development time and ensuring consistency.
|
|
15
|
+
- **Composability**: Plugins work together seamlessly. You can combine multiple plugins—an authentication plugin with a logging plugin and a database plugin—and they'll integrate naturally into your CLI's lifecycle.
|
|
16
|
+
|
|
17
|
+
### How Plugins Work
|
|
18
|
+
|
|
19
|
+
Plugins integrate at specific points in your CLI's execution:
|
|
20
|
+
|
|
21
|
+
1. **Registration**: When your CLI starts, plugins are registered and their dependencies are resolved
|
|
22
|
+
2. **Setup**: Plugins initialize and configure themselves, adding any global options (like `--debug`)
|
|
23
|
+
3. **Extension**: Plugins extend your command context with new functionality (like translation methods or API clients)
|
|
24
|
+
4. **Decoration**: Plugins can modify how commands execute or how help text is displayed
|
|
25
|
+
5. **Execution**: Your commands run with all plugin enhancements seamlessly integrated
|
|
26
|
+
|
|
27
|
+
This lifecycle integration means plugins can:
|
|
28
|
+
|
|
29
|
+
- Add global options available to all commands (like `--debug` or `--verbose`)
|
|
30
|
+
- Provide utilities accessible in any command (like API clients or database connections)
|
|
31
|
+
- Modify how your CLI displays help text or handles errors
|
|
32
|
+
- Intercept command execution for logging or validation
|
|
33
|
+
|
|
34
|
+
The beauty of Gunshi's plugin system is that it handles all the complexity behind the scenes.
|
|
35
|
+
|
|
36
|
+
You simply declare which plugins you want to use, and they automatically become part of your CLI's capabilities.
|
|
37
|
+
|
|
38
|
+
Now that you understand how plugins work, let's explore the plugins that come with Gunshi.
|
|
39
|
+
|
|
40
|
+
These built-in plugins provide essential CLI functionality out of the box.
|
|
41
|
+
|
|
42
|
+
## Built-in Plugins
|
|
43
|
+
|
|
44
|
+
Gunshi provides a standard `cli()` function that comes pre-configured with two essential plugins.
|
|
45
|
+
|
|
46
|
+
These built-in plugins give your CLI the familiar behavior users expect from command-line tools.
|
|
47
|
+
|
|
48
|
+
Let's explore what each plugin provides:
|
|
49
|
+
|
|
50
|
+
### @gunshi/plugin-global
|
|
51
|
+
|
|
52
|
+
This plugin adds `--help` and `--version` options to all your commands automatically.
|
|
53
|
+
|
|
54
|
+
When either option is used, the plugin intercepts command execution to display the appropriate information.
|
|
55
|
+
|
|
56
|
+
The following example demonstrates how the cli() function automatically includes the global plugin:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import { cli, define } from 'gunshi'
|
|
60
|
+
|
|
61
|
+
const command = define({
|
|
62
|
+
name: 'app',
|
|
63
|
+
description: 'My CLI application',
|
|
64
|
+
run: ctx => {
|
|
65
|
+
console.log('Running application')
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
await cli(process.argv.slice(2), command, {
|
|
70
|
+
name: 'my-app',
|
|
71
|
+
version: '1.0.0'
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Now your CLI automatically supports:
|
|
76
|
+
|
|
77
|
+
- `my-app --help` displays usage information rendered by the renderer plugin
|
|
78
|
+
- `my-app --version` displays the version number
|
|
79
|
+
- The plugin decorates command execution to handle these options before your command runs
|
|
80
|
+
|
|
81
|
+
### @gunshi/plugin-renderer
|
|
82
|
+
|
|
83
|
+
The renderer plugin automatically formats help text, usage information, and validation errors.
|
|
84
|
+
|
|
85
|
+
It works behind the scenes to ensure consistent, readable output across your entire CLI.
|
|
86
|
+
|
|
87
|
+
## Using Optional Plugins
|
|
88
|
+
|
|
89
|
+
Optional plugins add specialized features to your CLI.
|
|
90
|
+
|
|
91
|
+
Install them separately and configure them based on your needs.
|
|
92
|
+
|
|
93
|
+
### Internationalization
|
|
94
|
+
|
|
95
|
+
Add multi-language support with the i18n plugin:
|
|
96
|
+
|
|
97
|
+
::: code-group
|
|
98
|
+
|
|
99
|
+
```sh [npm]
|
|
100
|
+
npm install --save @gunshi/plugin-i18n
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```sh [pnpm]
|
|
104
|
+
pnpm add @gunshi/plugin-i18n
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```sh [yarn]
|
|
108
|
+
yarn add @gunshi/plugin-i18n
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```sh [deno]
|
|
112
|
+
# For Deno projects, you can add Gunshi from JSR:
|
|
113
|
+
deno add jsr:@gunshi/plugin-i18n
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```sh [bun]
|
|
117
|
+
bun add @gunshi/plugin-i18n
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
:::
|
|
121
|
+
|
|
122
|
+
Here's a simple example using the `defineI18n` helper:
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
import { cli } from 'gunshi'
|
|
126
|
+
import i18n, { defineI18n, resolveKey } from '@gunshi/plugin-i18n'
|
|
127
|
+
|
|
128
|
+
// Define resources inline for each locale
|
|
129
|
+
const resources = {
|
|
130
|
+
'en-US': {
|
|
131
|
+
greeting: 'Hello, {$name}!',
|
|
132
|
+
farewell: 'Goodbye!'
|
|
133
|
+
},
|
|
134
|
+
'ja-JP': {
|
|
135
|
+
greeting: 'こんにちは、{$name}さん!',
|
|
136
|
+
farewell: 'さようなら!'
|
|
137
|
+
},
|
|
138
|
+
'es-ES': {
|
|
139
|
+
greeting: '¡Hola, {$name}!',
|
|
140
|
+
farewell: '¡Adiós!'
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const command = defineI18n({
|
|
145
|
+
name: 'greet',
|
|
146
|
+
description: 'Greet someone in their language',
|
|
147
|
+
// Resource function returns translations for the current locale
|
|
148
|
+
resource: async ctx => {
|
|
149
|
+
const locale = ctx.extensions['g:i18n'].locale
|
|
150
|
+
return resources[locale] || resources['en-US']
|
|
151
|
+
},
|
|
152
|
+
args: {
|
|
153
|
+
name: {
|
|
154
|
+
type: 'string',
|
|
155
|
+
required: true,
|
|
156
|
+
description: 'Name to greet'
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
run: ctx => {
|
|
160
|
+
// Access the i18n plugin extension via its namespaced ID 'g:i18n'
|
|
161
|
+
// (All Gunshi plugins use the 'g:' prefix to prevent naming conflicts)
|
|
162
|
+
const t = ctx.extensions['g:i18n'].translate
|
|
163
|
+
console.log(t(resolveKey('greeting', ctx), { name: ctx.values.name }))
|
|
164
|
+
console.log(t(resolveKey('farewell', ctx)))
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
await cli(process.argv.slice(2), command, {
|
|
169
|
+
name: 'greet-cli',
|
|
170
|
+
version: '1.0.0',
|
|
171
|
+
plugins: [
|
|
172
|
+
i18n({
|
|
173
|
+
locale: process.env.LANG || 'en-US'
|
|
174
|
+
})
|
|
175
|
+
]
|
|
176
|
+
})
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
180
|
+
|
|
181
|
+
> [!NOTE]
|
|
182
|
+
> Plugin IDs use namespacing to prevent conflicts and identify ownership. Official Gunshi plugins use the `g:` prefix (e.g., `g:i18n`, `g:completion`). When developing your own plugins, use your organization's namespace (e.g., `myorg:logger`) or scoped package format (e.g., `@company/auth`). For detailed naming conventions and guidelines, see the [Plugin ID Guidelines](../plugin/guidelines.md#plugin-ids).
|
|
183
|
+
|
|
184
|
+
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
185
|
+
|
|
186
|
+
Key benefits:
|
|
187
|
+
|
|
188
|
+
- The `defineI18n` helper simplifies i18n setup for commands
|
|
189
|
+
- Automatic locale detection from system settings
|
|
190
|
+
- Simple interpolation syntax for dynamic values
|
|
191
|
+
- Fallback to default locale when translation is missing
|
|
192
|
+
- Plugin functionality is accessible via `ctx.extensions` in your command runners
|
|
193
|
+
|
|
194
|
+
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
195
|
+
|
|
196
|
+
> [!NOTE]
|
|
197
|
+
> The `ctx.extensions` object is how plugins extend your command context with additional functionality. The i18n plugin adds translation capabilities through `ctx.extensions['g:i18n']`. To learn more about working with plugin extensions and best practices for accessing them, see the [Context Extensions guide](../advanced/context-extensions.md).
|
|
198
|
+
|
|
199
|
+
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
200
|
+
|
|
201
|
+
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
202
|
+
|
|
203
|
+
> [!TIP]
|
|
204
|
+
> This example demonstrates basic internationalization setup. For comprehensive coverage including external resource files, TypeScript support, dynamic locale switching, and production deployment strategies, see the [Advanced Internationalization Guide](../advanced/internationalization.md).
|
|
205
|
+
|
|
206
|
+
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
207
|
+
|
|
208
|
+
### Shell Completion
|
|
209
|
+
|
|
210
|
+
Enable tab completion across different shells:
|
|
211
|
+
|
|
212
|
+
::: code-group
|
|
213
|
+
|
|
214
|
+
```sh [npm]
|
|
215
|
+
npm install --save @gunshi/plugin-completion
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```sh [pnpm]
|
|
219
|
+
pnpm add @gunshi/plugin-completion
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```sh [yarn]
|
|
223
|
+
yarn add @gunshi/plugin-completion
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
:::
|
|
227
|
+
|
|
228
|
+
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
229
|
+
|
|
230
|
+
> [!IMPORTANT]
|
|
231
|
+
> Shell completion currently requires Node.js. The completion feature is not available when running your CLI with Deno or Bun runtimes.
|
|
232
|
+
|
|
233
|
+
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
234
|
+
|
|
235
|
+
Here's how to add completion support:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
import { cli, define } from 'gunshi'
|
|
239
|
+
import completion from '@gunshi/plugin-completion'
|
|
240
|
+
|
|
241
|
+
const command = define({
|
|
242
|
+
name: 'deploy',
|
|
243
|
+
args: {
|
|
244
|
+
environment: {
|
|
245
|
+
type: 'string',
|
|
246
|
+
required: true,
|
|
247
|
+
description: 'Target environment'
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
run: ctx => {
|
|
251
|
+
console.log(`Deploying to ${ctx.values.environment}`)
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
await cli(process.argv.slice(2), command, {
|
|
256
|
+
name: 'deploy-cli',
|
|
257
|
+
version: '1.0.0',
|
|
258
|
+
plugins: [
|
|
259
|
+
completion({
|
|
260
|
+
config: {
|
|
261
|
+
entry: {
|
|
262
|
+
args: {
|
|
263
|
+
environment: {
|
|
264
|
+
handler: () => [
|
|
265
|
+
{ value: 'production', description: 'Production' },
|
|
266
|
+
{ value: 'staging', description: 'Staging' },
|
|
267
|
+
{ value: 'development', description: 'Development' }
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
]
|
|
275
|
+
})
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The completion plugin adds a special `complete` command to your CLI that generates shell-specific completion scripts.
|
|
279
|
+
|
|
280
|
+
### Installing Completion for End Users
|
|
281
|
+
|
|
282
|
+
Once you've added the completion plugin to your CLI, your users need to perform a one-time setup to enable tab completion on their system.
|
|
283
|
+
|
|
284
|
+
#### Basic Setup Example (Bash)
|
|
285
|
+
|
|
286
|
+
Users generate a completion script for their shell and add it to their shell configuration:
|
|
287
|
+
|
|
288
|
+
```sh
|
|
289
|
+
# Generate completion script and save it
|
|
290
|
+
deploy-cli complete bash > ~/.local/share/bash-completion/completions/deploy-cli
|
|
291
|
+
|
|
292
|
+
# Reload your shell configuration
|
|
293
|
+
source ~/.bashrc
|
|
294
|
+
|
|
295
|
+
# Now tab completion works!
|
|
296
|
+
deploy-cli dep<TAB> # Completes to: deploy-cli deploy
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
300
|
+
|
|
301
|
+
> [!TIP]
|
|
302
|
+
> For detailed setup instructions for all supported shells (Bash, Zsh, Fish, PowerShell), including directory creation and configuration steps, see the [@gunshi/plugin-completion README](https://github.com/kazupon/gunshi/tree/main/packages/plugin-completion#shell-completion-setup).
|
|
303
|
+
|
|
304
|
+
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
305
|
+
|
|
306
|
+
#### How It Works
|
|
307
|
+
|
|
308
|
+
The completion plugin adds a special `complete` command to your CLI that generates shell-specific completion scripts.
|
|
309
|
+
|
|
310
|
+
Users run this command once to generate the script for their shell, then source it in their shell configuration to enable tab completion.
|
|
311
|
+
|
|
312
|
+
## Combining Plugins
|
|
313
|
+
|
|
314
|
+
Plugins work seamlessly together. Here's an example using both i18n and completion:
|
|
315
|
+
|
|
316
|
+
```js
|
|
317
|
+
import { cli } from 'gunshi'
|
|
318
|
+
import i18n, { defineI18n, resolveKey } from '@gunshi/plugin-i18n'
|
|
319
|
+
import completion from '@gunshi/plugin-completion'
|
|
320
|
+
|
|
321
|
+
// Define your resources
|
|
322
|
+
const resources = {
|
|
323
|
+
'en-US': {
|
|
324
|
+
start: 'Starting build for {$mode} mode...',
|
|
325
|
+
success: 'Build completed successfully!'
|
|
326
|
+
},
|
|
327
|
+
'ja-JP': {
|
|
328
|
+
start: '{$mode}モードでビルドを開始しています...',
|
|
329
|
+
success: 'ビルドが正常に完了しました!'
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const buildCommand = defineI18n({
|
|
334
|
+
name: 'build',
|
|
335
|
+
description: 'Build the project',
|
|
336
|
+
resource: async ctx => {
|
|
337
|
+
const locale = ctx.extensions['g:i18n'].locale
|
|
338
|
+
return resources[locale] || resources['en-US']
|
|
339
|
+
},
|
|
340
|
+
args: {
|
|
341
|
+
mode: {
|
|
342
|
+
type: 'string',
|
|
343
|
+
default: 'development',
|
|
344
|
+
description: 'Build mode'
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
run: ctx => {
|
|
348
|
+
const t = ctx.extensions['g:i18n'].translate
|
|
349
|
+
console.log(t(resolveKey('start', ctx), { mode: ctx.values.mode }))
|
|
350
|
+
// Build logic here
|
|
351
|
+
console.log(t(resolveKey('success', ctx)))
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
await cli(process.argv.slice(2), buildCommand, {
|
|
356
|
+
name: 'build-tool',
|
|
357
|
+
version: '2.0.0',
|
|
358
|
+
plugins: [
|
|
359
|
+
i18n({
|
|
360
|
+
locale: process.env.LANG || 'en-US'
|
|
361
|
+
}),
|
|
362
|
+
completion({
|
|
363
|
+
config: {
|
|
364
|
+
entry: {
|
|
365
|
+
args: {
|
|
366
|
+
mode: {
|
|
367
|
+
handler: () => [
|
|
368
|
+
{ value: 'development', description: 'Development build' },
|
|
369
|
+
{ value: 'production', description: 'Production build' }
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
]
|
|
377
|
+
})
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Both plugins enhance your CLI without interfering with each other - i18n handles translations while completion provides tab suggestions.
|
|
381
|
+
|
|
382
|
+
## Plugin Configuration
|
|
383
|
+
|
|
384
|
+
Configure plugins based on your environment or user preferences.
|
|
385
|
+
|
|
386
|
+
### Environment-based Configuration
|
|
387
|
+
|
|
388
|
+
Load plugins conditionally:
|
|
389
|
+
|
|
390
|
+
```js
|
|
391
|
+
import { cli } from 'gunshi'
|
|
392
|
+
|
|
393
|
+
const plugins = []
|
|
394
|
+
|
|
395
|
+
// Add completion in development only
|
|
396
|
+
if (process.env.NODE_ENV === 'development') {
|
|
397
|
+
const completion = await import('@gunshi/plugin-completion')
|
|
398
|
+
plugins.push(completion.default())
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Add i18n if locale is set
|
|
402
|
+
if (process.env.LANG) {
|
|
403
|
+
const i18n = await import('@gunshi/plugin-i18n')
|
|
404
|
+
plugins.push(i18n.default({ locale: process.env.LANG }))
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
await cli(process.argv.slice(2), command, {
|
|
408
|
+
name: 'my-cli',
|
|
409
|
+
plugins
|
|
410
|
+
})
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
This approach keeps your production builds lean while providing developer features during development.
|
|
414
|
+
|
|
415
|
+
## Minimal Setup
|
|
416
|
+
|
|
417
|
+
If you need precise control over your CLI's functionality and bundle size, you can use `@gunshi/bone` - Gunshi's bare-bones foundation package.
|
|
418
|
+
|
|
419
|
+
Unlike the standard `cli()` function which includes plugins automatically, `@gunshi/bone` starts with zero plugins, letting you add only what you need:
|
|
420
|
+
|
|
421
|
+
::: code-group
|
|
422
|
+
|
|
423
|
+
```sh [npm]
|
|
424
|
+
npm install --save @gunshi/bone @gunshi/plugin-global @gunshi/plugin-renderer
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
```sh [pnpm]
|
|
428
|
+
pnpm add @gunshi/bone @gunshi/plugin-global @gunshi/plugin-renderer
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
```sh [yarn]
|
|
432
|
+
yarn add @gunshi/bone @gunshi/plugin-global @gunshi/plugin-renderer
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
:::
|
|
436
|
+
|
|
437
|
+
```js
|
|
438
|
+
import { cli } from '@gunshi/bone'
|
|
439
|
+
import global from '@gunshi/plugin-global'
|
|
440
|
+
import renderer from '@gunshi/plugin-renderer'
|
|
441
|
+
|
|
442
|
+
// Only includes plugins you explicitly add
|
|
443
|
+
await cli(process.argv.slice(2), command, {
|
|
444
|
+
name: 'minimal-cli',
|
|
445
|
+
plugins: [
|
|
446
|
+
global(), // Adds --help and --version options
|
|
447
|
+
renderer() // Adds help text formatting
|
|
448
|
+
]
|
|
449
|
+
})
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
This approach gives you explicit control over every aspect of your CLI, making it ideal for applications where bundle size or specific feature control is critical.
|
|
453
|
+
|
|
454
|
+
Common use cases for `@gunshi/bone`:
|
|
455
|
+
|
|
456
|
+
- Embedded CLIs with size constraints
|
|
457
|
+
- Custom implementations of help or version handling
|
|
458
|
+
- Testing plugin interactions in isolation
|
|
459
|
+
- Applications requiring precise control over all CLI behavior
|
|
460
|
+
|
|
461
|
+
## Next Steps
|
|
462
|
+
|
|
463
|
+
Now that you understand the plugin system basics, you can explore more advanced topics and start building with Gunshi's plugin ecosystem.
|
|
464
|
+
|
|
465
|
+
To deepen your understanding of the plugin system:
|
|
466
|
+
|
|
467
|
+
- **TypeScript Support**: Explore type-safe plugin usage in the [Type System Guide](../advanced/type-system.md)
|
|
468
|
+
- **Context Extensions**: Learn how plugins extend functionality through [Context Extensions](../advanced/context-extensions.md)
|
|
469
|
+
|
|
470
|
+
Ready to create your own plugins?
|
|
471
|
+
|
|
472
|
+
- **Plugin Development**: Build custom plugins with the [Plugin Development Guide](../plugin/introduction.md)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Type Safe
|
|
2
|
+
|
|
3
|
+
In the previous chapter, we learned how to create commands using declarative configuration with plain JavaScript objects.
|
|
4
|
+
|
|
5
|
+
While this approach works well, TypeScript users can benefit from enhanced type safety and better development experience using Gunshi's `define` function.
|
|
6
|
+
|
|
7
|
+
The `define` function wraps your command configuration and provides automatic type inference, ensuring that your command handlers receive properly typed context objects without manual type annotations.
|
|
8
|
+
|
|
9
|
+
## Benefits of Type Safety
|
|
10
|
+
|
|
11
|
+
Using TypeScript with Gunshi's `define` function provides CLI-specific advantages:
|
|
12
|
+
|
|
13
|
+
- **Autocompletion for command options**: IDE suggests available options when accessing `ctx.values`
|
|
14
|
+
- **Prevent runtime errors**: Catch typos in option names before your CLI ships
|
|
15
|
+
- **Self-documenting commands**: Types show exactly what arguments your command accepts
|
|
16
|
+
- **Safe refactoring**: Rename options across your codebase with confidence
|
|
17
|
+
|
|
18
|
+
## Type Safety Levels in Gunshi
|
|
19
|
+
|
|
20
|
+
Gunshi provides different levels of type safety to match your needs:
|
|
21
|
+
|
|
22
|
+
1. **Basic type inference** (covered in this chapter): Automatic typing of command arguments
|
|
23
|
+
2. **Plugin extension typing**: Type-safe access to plugin functionality
|
|
24
|
+
3. **Full type parameters**: Complete control over all types using `GunshiParams`
|
|
25
|
+
|
|
26
|
+
This chapter focuses on the first level, which covers most common use cases. Advanced patterns are available when you need them.
|
|
27
|
+
|
|
28
|
+
## Using `define` for Type Safety
|
|
29
|
+
|
|
30
|
+
The `define` function transforms your command configuration to provide:
|
|
31
|
+
|
|
32
|
+
- **Automatic type inference**: No need to manually type `ctx` parameters
|
|
33
|
+
- **IDE autocompletion**: Get suggestions for `ctx.values` properties
|
|
34
|
+
- **Compile-time validation**: TypeScript catches typos and type mismatches before runtime
|
|
35
|
+
- **Simplified imports**: No need to import type definitions like `Command` or `CommandContext`
|
|
36
|
+
|
|
37
|
+
Let's transform the greeting command from the previous chapter to use `define` for full type safety.
|
|
38
|
+
|
|
39
|
+
The `define` function is a simple wrapper that preserves your command's type information, enabling TypeScript to automatically infer types for your command options and provide IDE autocompletion:
|
|
40
|
+
|
|
41
|
+
```ts [cli.ts]
|
|
42
|
+
import { cli, define } from 'gunshi'
|
|
43
|
+
|
|
44
|
+
// Define a command using the `define` function
|
|
45
|
+
const command = define({
|
|
46
|
+
name: 'greet',
|
|
47
|
+
args: {
|
|
48
|
+
// Define a string option 'name' with a short alias 'n'
|
|
49
|
+
name: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
short: 'n',
|
|
52
|
+
description: 'Your name'
|
|
53
|
+
},
|
|
54
|
+
// Define a number option 'age' with a default value
|
|
55
|
+
age: {
|
|
56
|
+
type: 'number',
|
|
57
|
+
short: 'a',
|
|
58
|
+
description: 'Your age',
|
|
59
|
+
default: 30
|
|
60
|
+
},
|
|
61
|
+
// Define a boolean flag 'verbose'
|
|
62
|
+
verbose: {
|
|
63
|
+
type: 'boolean',
|
|
64
|
+
short: 'V',
|
|
65
|
+
description: 'Enable verbose output'
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
// The 'ctx' parameter is automatically typed based on the args
|
|
69
|
+
run: ctx => {
|
|
70
|
+
// `ctx.values` is fully typed!
|
|
71
|
+
const { name, age, verbose } = ctx.values
|
|
72
|
+
|
|
73
|
+
// TypeScript knows the types:
|
|
74
|
+
// - name: string | undefined (undefined if not provided)
|
|
75
|
+
// - age: number (always a number due to the default)
|
|
76
|
+
// - verbose: boolean | undefined (undefined if not provided, true if --verbose flag is used)
|
|
77
|
+
|
|
78
|
+
let greeting = `Hello, ${name || 'stranger'}!`
|
|
79
|
+
// age always has a value due to the default
|
|
80
|
+
greeting += ` You are ${age} years old.`
|
|
81
|
+
|
|
82
|
+
console.log(greeting)
|
|
83
|
+
|
|
84
|
+
if (verbose) {
|
|
85
|
+
console.log('Verbose mode enabled.')
|
|
86
|
+
console.log('Parsed values:', ctx.values)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Execute the command
|
|
92
|
+
await cli(process.argv.slice(2), command)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
96
|
+
|
|
97
|
+
> [!TIP]
|
|
98
|
+
> The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/essentials/type-safe).
|
|
99
|
+
|
|
100
|
+
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
101
|
+
|
|
102
|
+
With `define`:
|
|
103
|
+
|
|
104
|
+
- You don't need to import types like `Command` or `CommandContext`.
|
|
105
|
+
- The `ctx` parameter in the `run` function automatically gets the correct type, derived from the `args` definition.
|
|
106
|
+
- Accessing `ctx.values.optionName` provides type safety and autocompletion based on the option's `type` and whether it has a `default`.
|
|
107
|
+
- Options without a `default` (like `name`) are typed as `T | undefined`.
|
|
108
|
+
- Options with a `default` (like `age`) are typed simply as `T`.
|
|
109
|
+
- Boolean flags without a `default` (like `verbose`) are typed as `boolean | undefined`.
|
|
110
|
+
|
|
111
|
+
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
112
|
+
|
|
113
|
+
> [!NOTE]
|
|
114
|
+
> For boolean options that need both positive and negative forms (e.g., `--verbose` and `--no-verbose`), see the [Negatable Boolean Options](./declarative.md#negatable-boolean-options) section in the declarative configuration guide.
|
|
115
|
+
|
|
116
|
+
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
117
|
+
|
|
118
|
+
This approach significantly simplifies creating type-safe CLIs with Gunshi.
|
|
119
|
+
|
|
120
|
+
## When to Use `define`
|
|
121
|
+
|
|
122
|
+
Use the `define` function when:
|
|
123
|
+
|
|
124
|
+
- You're writing TypeScript and want automatic type inference
|
|
125
|
+
- You need IDE autocompletion for command context
|
|
126
|
+
- You want to catch type-related errors at compile time
|
|
127
|
+
|
|
128
|
+
Use plain objects (as shown in the previous chapter) when:
|
|
129
|
+
|
|
130
|
+
- You're writing plain JavaScript
|
|
131
|
+
- You prefer explicit type annotations
|
|
132
|
+
- You're integrating with existing type definitions
|
|
133
|
+
|
|
134
|
+
## Advanced Type Parameters
|
|
135
|
+
|
|
136
|
+
While the examples above show the simplest form of the `define` function, Gunshi provides more advanced type parameter patterns for complex scenarios:
|
|
137
|
+
|
|
138
|
+
- **Plugin extensions**: Type-safe access to plugin-provided functionality
|
|
139
|
+
- **Explicit argument types**: Fine-grained control over type inference
|
|
140
|
+
- **GunshiParams utility**: Combined typing of arguments and extensions
|
|
141
|
+
|
|
142
|
+
These advanced patterns are covered in detail in the [Advanced Type System](../advanced/type-system.md) documentation.
|
|
143
|
+
|
|
144
|
+
For most commands, the basic `define` usage shown above provides sufficient type safety.
|
|
145
|
+
|
|
146
|
+
## Next Steps
|
|
147
|
+
|
|
148
|
+
Now that you understand how to create type-safe commands with `define`, you're ready to explore more advanced features:
|
|
149
|
+
|
|
150
|
+
- **Composable Sub-commands**: Learn how type safety extends to multi-command CLIs
|
|
151
|
+
- **Plugin System**: Discover how plugins maintain type safety across extensions
|
|
152
|
+
- **Advanced Type System**: For complex scenarios, Gunshi offers additional type parameters and patterns (covered in the [Advanced Type System](../advanced/type-system.md) documentation)
|
|
153
|
+
|
|
154
|
+
In the next chapter, we'll explore how to create [composable sub-commands](./composable.md) while maintaining the type safety we've established here.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Setup
|
|
2
|
+
|
|
3
|
+
Gunshi can be installed in various JavaScript environments. Choose the installation method that matches your project setup.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
### Stable
|
|
8
|
+
|
|
9
|
+
::: code-group
|
|
10
|
+
|
|
11
|
+
```sh [npm]
|
|
12
|
+
npm install --save gunshi
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```sh [pnpm]
|
|
16
|
+
pnpm add gunshi
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```sh [yarn]
|
|
20
|
+
yarn add gunshi
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```sh [deno]
|
|
24
|
+
# For deno projects, you can add Gunshi from JSR:
|
|
25
|
+
deno add jsr:@gunshi/gunshi
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```sh [bun]
|
|
29
|
+
bun add gunshi
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
:::
|
|
33
|
+
|
|
34
|
+
### v0.27 Beta
|
|
35
|
+
|
|
36
|
+
::: code-group
|
|
37
|
+
|
|
38
|
+
```sh [npm]
|
|
39
|
+
npm install --save gunshi@beta
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```sh [pnpm]
|
|
43
|
+
pnpm add gunshi@beta
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```sh [yarn]
|
|
47
|
+
yarn add gunshi@beta
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```sh [deno]
|
|
51
|
+
## you can specify version with `@`
|
|
52
|
+
deno add jsr:@gunshi/gunshi@0.27.2
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```sh [bun]
|
|
56
|
+
bun add gunshi@beta
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Requirements
|
|
60
|
+
|
|
61
|
+
Gunshi requires:
|
|
62
|
+
|
|
63
|
+
- **JavaScript Runtime**:
|
|
64
|
+
- **Node.js**: v20 or later
|
|
65
|
+
- **Deno**: v2 or later
|
|
66
|
+
- **Bun**: v1.1 or later
|
|
67
|
+
- **ES Modules**: `"type": "module"` in `package.json` (if using Node.js and Bun)
|
|
68
|
+
- **TypeScript**: Version 5.0 or higher (if using TypeScript)
|