@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,432 @@
1
+ # Plugin Lifecycle
2
+
3
+ Understanding the Gunshi lifecycle is crucial for effective plugin development.
4
+
5
+ This guide explains how plugins integrate with the CLI execution flow and when different plugin features are activated.
6
+
7
+ ## CLI Execution Lifecycle
8
+
9
+ When a Gunshi CLI application runs, it follows a specific sequence of steps from startup to completion.
10
+
11
+ The diagram below shows the complete execution flow, with plugin-related steps highlighted in green:
12
+
13
+ ```mermaid
14
+ graph TD
15
+ A[A. CLI Start] --> B[B. Load Plugins]
16
+ B --> C[C. Resolve Dependencies]
17
+ C --> D[D. Execute Plugin Setup]
18
+ D --> E[E. Parse Arguments]
19
+ E --> F[F. Resolve Command]
20
+ F --> G[G. Resolve & Validate Args]
21
+ G --> H[H. Create CommandContext<br/>Process each plugin sequentially:<br/>1. Create extension<br/>2. Call onExtension]
22
+ H --> I[I. Execute Command]
23
+ I --> J[J. CLI End]
24
+
25
+ style B fill:#468c56,color:white
26
+ style C fill:#468c56,color:white
27
+ style D fill:#468c56,color:white
28
+ style H fill:#468c56,color:white
29
+ style I fill:#468c56,color:white
30
+ ```
31
+
32
+ The lifecycle consists of 10 steps (A through J), where plugins are primarily involved in:
33
+
34
+ - **Steps B-D**: Plugin initialization and setup
35
+ - **Step H**: Plugin extension creation and activation during CommandContext creation
36
+ - **Step I**: Command execution with plugin decorators applied
37
+
38
+ ## Plugin-Specific Lifecycle Steps
39
+
40
+ Plugins are primarily involved in specific steps of the CLI lifecycle.
41
+
42
+ This section focuses on the steps where plugins actively participate.
43
+
44
+ ### Setup Phase (Steps B-D)
45
+
46
+ During the setup phase, plugins are loaded, dependencies are resolved, and plugin setup functions are executed.
47
+
48
+ This phase occurs once at CLI initialization.
49
+
50
+ **What happens in this phase:**
51
+
52
+ - Plugins configure the CLI by adding options, commands, and decorators
53
+ - All modifications are registered but not yet executed
54
+ - The `setup()` function runs for each plugin
55
+
56
+ #### Step B: Load Plugins
57
+
58
+ Plugins are collected from CLI options and prepared for initialization.
59
+
60
+ The following code shows how plugins are passed to the CLI function:
61
+
62
+ ```js
63
+ import { cli } from 'gunshi'
64
+
65
+ await cli(args, command, {
66
+ plugins: [
67
+ plugin1(), // Collected
68
+ plugin2(), // Collected
69
+ plugin3() // Collected
70
+ ]
71
+ })
72
+ ```
73
+
74
+ #### Step C: Resolve Dependencies
75
+
76
+ Gunshi uses **topological sorting** to resolve plugin dependencies.
77
+
78
+ The following example demonstrates how plugins with dependencies are resolved in the correct order:
79
+
80
+ ```js
81
+ import { plugin } from 'gunshi/plugin'
82
+
83
+ // Given these plugins:
84
+ const pluginA = plugin({
85
+ id: 'a',
86
+ dependencies: ['b', 'c']
87
+ })
88
+
89
+ const pluginB = plugin({
90
+ id: 'b',
91
+ dependencies: ['d']
92
+ })
93
+
94
+ const pluginC = plugin({
95
+ id: 'c'
96
+ })
97
+
98
+ const pluginD = plugin({
99
+ id: 'd'
100
+ })
101
+
102
+ // Resolution order: d → b → c → a
103
+ ```
104
+
105
+ <!-- eslint-disable markdown/no-missing-label-refs -->
106
+
107
+ > [!TIP]
108
+ > For details on plugin dependency resolution, including circular dependency detection, optional dependencies, see [Plugin Dependencies](./dependencies.md).
109
+
110
+ <!-- eslint-enable markdown/no-missing-label-refs -->
111
+
112
+ #### Step D: Execute Plugin Setup
113
+
114
+ The `setup` function of each plugin is called in dependency order.
115
+
116
+ This example shows what actions a plugin can perform during setup:
117
+
118
+ ```js
119
+ import { plugin } from 'gunshi/plugin'
120
+
121
+ const myPlugin = plugin({
122
+ id: 'my-plugin',
123
+ setup: ctx => {
124
+ // This runs during Setup Phase
125
+ console.log('Plugin setting up')
126
+
127
+ // Add global options
128
+ ctx.addGlobalOption('verbose', {
129
+ type: 'boolean',
130
+ description: 'Verbose output'
131
+ })
132
+
133
+ // Register sub-commands
134
+ ctx.addCommand('plugin-cmd', {
135
+ name: 'plugin-cmd',
136
+ run: ctx => console.log('Plugin command')
137
+ })
138
+
139
+ // Add decorators (they stack in LIFO order)
140
+ ctx.decorateCommand(baseRunner => async ctx => {
141
+ console.log('Before command (from plugin)')
142
+ const result = await baseRunner(ctx)
143
+ console.log('After command (from plugin)')
144
+ return result
145
+ })
146
+ }
147
+ })
148
+ ```
149
+
150
+ ### Command Processing (Steps E-H)
151
+
152
+ Between the setup phase and execution phase, Gunshi processes the command-line arguments and prepares the execution context:
153
+
154
+ - **Step E**: Parse command-line arguments into structured tokens
155
+ - **Step F**: Resolve which command to execute
156
+ - **Step G**: Validate arguments against the command's schema
157
+ - **Step H**: Create the CommandContext object
158
+
159
+ <!-- eslint-disable markdown/no-missing-label-refs -->
160
+
161
+ > [!NOTE]
162
+ > These internal processing steps are handled automatically by Gunshi. Plugin developers don't need to interact with these steps directly.
163
+
164
+ <!-- eslint-enable markdown/no-missing-label-refs -->
165
+
166
+ ### Execution Phase (Steps H-I)
167
+
168
+ During the execution phase, the CommandContext is created with plugin extensions, and then the command is executed with all decorators applied.
169
+
170
+ ### Step H: Create CommandContext & Process Plugins
171
+
172
+ The CommandContext is created and each plugin's extension is initialized:
173
+
174
+ 1. Initialize CommandContext with parsed arguments and values
175
+ 2. For each plugin (in dependency order):
176
+ - Call the plugin's `extension()` function to create an extension
177
+ - Store the extension in `context.extensions[pluginId]`
178
+ - Immediately call the plugin's `onExtension()` callback if present
179
+
180
+ This sequential processing ensures extensions from dependencies are available to dependent plugins.
181
+
182
+ <!-- eslint-disable markdown/no-missing-label-refs -->
183
+
184
+ > [!NOTE]
185
+ > For detailed information about the extension lifecycle, including execution order guarantees, the relationship between `extension` and `onExtension`, and code examples, see the [Extension Lifecycle](./extensions.md#extension-lifecycle) section in the Plugin Extensions guide.
186
+
187
+ <!-- eslint-enable markdown/no-missing-label-refs -->
188
+
189
+ ### Step I: Execute Command
190
+
191
+ The command runner executes with:
192
+
193
+ - All decorators applied in LIFO (Last In, First Out) order
194
+ - Full access to all plugin extensions via `ctx.extensions`
195
+ - Complete CommandContext with validated arguments
196
+
197
+ This example illustrates the command execution with decorator wrapping and extension usage:
198
+
199
+ ```js
200
+ import { define } from 'gunshi'
201
+
202
+ // If plugins A, B, C add decorators in that order:
203
+ // Execution order: C → B → A → original command → A → B → C
204
+
205
+ const command = define({
206
+ name: 'build',
207
+ run: ctx => {
208
+ // This is the original command
209
+ ctx.extensions.logger.log('Building project...')
210
+ // Build logic here
211
+ }
212
+ })
213
+ ```
214
+
215
+ ## Extension Lifecycle in Detail
216
+
217
+ Understanding the relationship between `extension` and `onExtension` is crucial for effective plugin development.
218
+
219
+ During Step H (Create CommandContext):
220
+
221
+ - Each plugin is processed sequentially in dependency order
222
+ - For each plugin: the `extension` factory is called, then immediately its `onExtension` callback
223
+ - This sequential approach ensures that when a plugin's `onExtension` runs, all previous plugins' extensions are already available through `ctx.extensions`
224
+
225
+ <!-- eslint-disable markdown/no-missing-label-refs -->
226
+
227
+ > [!TIP]
228
+ > For a detailed visual representation of the extension lifecycle and execution order guarantees, see [Extension Lifecycle](./extensions.md#extension-lifecycle) in the Plugin Extensions guide.
229
+
230
+ <!-- eslint-enable markdown/no-missing-label-refs -->
231
+
232
+ ## Lifecycle with Command Hooks
233
+
234
+ Gunshi provides Command hooks (`onBeforeCommand`, `onAfterCommand`, `onErrorCommand`) that integrate with the plugin lifecycle.
235
+
236
+ The following sequence diagram illustrates how these command hooks interact with plugins during command execution:
237
+
238
+ <!-- eslint-disable markdown/no-missing-label-refs -->
239
+
240
+ > [!TIP]
241
+ > For details on Command Hooks, including advanced use cases like logging, performance monitoring, validation guards, and transaction management, see [Command Hooks](../advanced/command-hooks.md).
242
+
243
+ <!-- eslint-enable markdown/no-missing-label-refs -->
244
+
245
+ ```mermaid
246
+ sequenceDiagram
247
+ participant CLI as CLI
248
+ participant Plugin as Plugin
249
+ participant Command as Command
250
+
251
+ CLI->>Plugin: Setup Phase
252
+ Plugin->>CLI: Register decorators
253
+
254
+ CLI->>CLI: Create Context
255
+ CLI->>Plugin: Create Extensions
256
+ Plugin->>CLI: Return Extensions
257
+
258
+ CLI->>CLI: onBeforeCommand
259
+ CLI->>Plugin: Apply Decorators
260
+ Plugin->>Command: Execute (decorated)
261
+ Command->>Plugin: Return
262
+ Plugin->>CLI: Return
263
+ CLI->>CLI: onAfterCommand
264
+
265
+ Note over CLI: Or on error:
266
+ Command-->>CLI: Throw Error
267
+ CLI->>CLI: onErrorCommand
268
+ ```
269
+
270
+ This sequence shows:
271
+
272
+ 1. **Setup Phase**: Plugins register their decorators during initialization
273
+ 2. **Extension Creation**: Plugin extensions are created and returned to the CLI
274
+ 3. **Command Hooks**: The `onBeforeCommand` hook runs before decorators and command execution
275
+ 4. **Decorated Execution**: Plugin decorators wrap the command execution
276
+ 5. **Post-Execution**: The `onAfterCommand` hook runs after successful completion
277
+ 6. **Error Handling**: The `onErrorCommand` hook catches any errors during execution
278
+
279
+ ## Complete Lifecycle Example
280
+
281
+ Here's a complete example showing all lifecycle phases including Command Hooks.
282
+
283
+ Plugin Codes:
284
+
285
+ ```js [lifecycle.js]
286
+ import { plugin } from 'gunshi/plugin'
287
+
288
+ export default plugin({
289
+ id: 'lifecycle',
290
+ dependencies: ['logger'], // Step C: Dependency resolution
291
+
292
+ // Step D: Setup execution
293
+ setup: ctx => {
294
+ console.log('1. lifecycle plugin setup phase started')
295
+
296
+ // Register global option
297
+ ctx.addGlobalOption('verbose', {
298
+ type: 'boolean',
299
+ alias: 'v',
300
+ description: 'Verbose output'
301
+ })
302
+
303
+ // Add sub-command
304
+ ctx.addCommand('status', {
305
+ name: 'status',
306
+ run: ctx => console.log('Status: OK')
307
+ })
308
+
309
+ // Register decorators (LIFO order)
310
+ ctx.decorateCommand(runner => async ctx => {
311
+ console.log('5. Command decorator (before)')
312
+ const result = await runner(ctx)
313
+ console.log('7. Command decorator (after)')
314
+ return result
315
+ })
316
+
317
+ console.log('2. Setup phase completed')
318
+ },
319
+
320
+ // Step H: Extension creation (during CommandContext creation)
321
+ extension: (ctx, cmd) => {
322
+ console.log('3. Extension created for:', cmd.name)
323
+
324
+ return {
325
+ demo: () => 'Hello from extension',
326
+ cleanup: () => console.log('9. Extension cleanup')
327
+ }
328
+ },
329
+
330
+ // Step H: Post-extension callback (immediately after extension creation)
331
+ onExtension: (ctx, cmd) => {
332
+ console.log('4. All extensions ready')
333
+ }
334
+ })
335
+ ```
336
+
337
+ Application Codes:
338
+
339
+ ```js [cli.js]
340
+ import { cli, define } from 'gunshi'
341
+ import logger from './logger.js'
342
+ import lifecycle from './lifecycle.js'
343
+
344
+ // Command definition
345
+ const command = define({
346
+ name: 'build',
347
+ args: {
348
+ fail: {
349
+ type: 'boolean'
350
+ }
351
+ },
352
+ run: ctx => {
353
+ console.log('6. Actual command execution')
354
+ console.log('Extension says:', ctx.extensions['lifecycle'].demo())
355
+
356
+ // Simulate an error for demonstration
357
+ if (ctx.values.fail) {
358
+ throw new Error('Build failed!')
359
+ }
360
+ }
361
+ })
362
+
363
+ // Running the CLI with Command Hooks
364
+ await cli(process.argv.slice(2), command, {
365
+ // Plugin installation
366
+ plugins: [logger, lifecycle],
367
+
368
+ // Command Hooks are defined at CLI level
369
+ onBeforeCommand: ctx => {
370
+ console.log('4.5. onBeforeCommand hook')
371
+ },
372
+
373
+ onAfterCommand: ctx => {
374
+ console.log('8. onAfterCommand hook')
375
+ // Cleanup can be done here
376
+ ctx.extensions['lifecycle'].cleanup()
377
+ },
378
+
379
+ onErrorCommand: (ctx, error) => {
380
+ console.log('8. onErrorCommand hook:', error.message)
381
+ // Error recovery or cleanup
382
+ ctx.extensions['lifecycle'].cleanup()
383
+ }
384
+ })
385
+ ```
386
+
387
+ <!-- eslint-disable markdown/no-missing-label-refs -->
388
+
389
+ > [!TIP]
390
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/plugins/lifecycle).
391
+
392
+ <!-- eslint-enable markdown/no-missing-label-refs -->
393
+
394
+ Run your application with plugin:
395
+
396
+ ```sh
397
+ node cli.js
398
+
399
+ logger plugin setup phase started
400
+ 1. lifecycle plugin setup phase started
401
+ 2. Setup phase completed
402
+ 3. Extension created for: build
403
+ 4. All extensions ready
404
+ 4.5. onBeforeCommand hook
405
+ 5. Command decorator (before)
406
+ 6. Actual command execution
407
+ Extension says: Hello from extension
408
+ 7. Command decorator (after)
409
+ 8. onAfterCommand hook
410
+ 9. Extension cleanup
411
+
412
+ node index.js --fail
413
+ logger plugin setup phase started
414
+ 1. lifecycle plugin setup phase started
415
+ 2. Setup phase completed
416
+ 3. Extension created for: build
417
+ 4. All extensions ready
418
+ 4.5. onBeforeCommand hook
419
+ 5. Command decorator (before)
420
+ 6. Actual command execution
421
+ Extension says: Hello from extension
422
+ 8. onErrorCommand hook: Build failed!
423
+ 9. Extension cleanup
424
+ file:///path/to/projects/gunshi/playground/plugins/lifecycle/index.js:19
425
+ throw new Error('Build failed!')
426
+ ```
427
+
428
+ ## Next Steps
429
+
430
+ Now that you understand how plugins integrate with the CLI lifecycle—from setup through command execution to cleanup—you're ready to explore how plugins can work together.
431
+
432
+ The next chapter, [Plugin Dependencies](./dependencies.md), will teach you how to build plugin ecosystems where plugins can depend on and interact with each other, enabling composition patterns for complex CLI applications.
@@ -0,0 +1,37 @@
1
+ # Plugin List
2
+
3
+ Gunshi provides official plugins that address common CLI development needs. See [Using Plugins](./getting-started.md) for how to use plugins in your application.
4
+
5
+ ## Official Plugins
6
+
7
+ ### [@gunshi/plugin-global](https://github.com/kazupon/gunshi/tree/main/packages/plugin-global)
8
+
9
+ Adds global `--help` and `--version` options to all commands.
10
+
11
+ ### [@gunshi/plugin-renderer](https://github.com/kazupon/gunshi/tree/main/packages/plugin-renderer)
12
+
13
+ Provides default rendering for usage, help text, and validation errors.
14
+
15
+ ### [@gunshi/plugin-i18n](https://github.com/kazupon/gunshi/tree/main/packages/plugin-i18n)
16
+
17
+ Provides internationalization support for CLI applications.
18
+
19
+ ### [@gunshi/plugin-completion](https://github.com/kazupon/gunshi/tree/main/packages/plugin-completion)
20
+
21
+ Provides shell completion functionality for bash, zsh, and fish.
22
+
23
+ ## Community Plugins
24
+
25
+ <!-- eslint-disable markdown/no-missing-label-refs -->
26
+
27
+ > [!NOTE]
28
+ > Welcome your plugins! Submit a PR to add your plugin to this list.
29
+
30
+ <!-- eslint-enable markdown/no-missing-label-refs -->
31
+
32
+ ## Where to Go From Here
33
+
34
+ - **Use official plugins**: Integrate these plugins into your CLI applications for enhanced functionality
35
+ - **Study implementations**: Review the source code of official plugins to learn implementation patterns
36
+ - **Create your own**: Apply what you've learned to build custom plugins for your specific needs
37
+ - **Share with the community**: Publish your plugins and submit a PR to add them to this list