@gunshi/plugin 0.27.0-beta.2 → 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/README.md +1 -1017
- package/lib/index.d.ts +32 -32
- package/lib/index.js +15 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -16,1023 +16,7 @@ This package provides comprehensive APIs and TypeScript type definitions for cre
|
|
|
16
16
|
|
|
17
17
|
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- [Overview](#-overview)
|
|
22
|
-
- [Features](#-features)
|
|
23
|
-
- [Installation](#-installation)
|
|
24
|
-
- [Quick Start](#-quick-start)
|
|
25
|
-
- [Gunshi Lifecycle and Plugins](#-gunshi-lifecycle-and-plugins)
|
|
26
|
-
- [Plugin in Depth](#-plugin-in-depth)
|
|
27
|
-
- [Plugin Communication & Collaboration](#-plugin-communication--collaboration)
|
|
28
|
-
- [Plugin Best Practices](#-plugin-best-practices)
|
|
29
|
-
- [Official Plugins](#-official-plugins)
|
|
30
|
-
- [Community Plugins](#-community-plugins)
|
|
31
|
-
- [API Reference](#-api-reference)
|
|
32
|
-
|
|
33
|
-
## 📜 Overview
|
|
34
|
-
|
|
35
|
-
Gunshi's plugin system is designed with a philosophy of **composability**, **type safety**, and **developer experience**. It allows you to extend CLI functionality in a modular way without modifying the core library.
|
|
36
|
-
|
|
37
|
-
### Why Plugins?
|
|
38
|
-
|
|
39
|
-
- **Separation of Concerns**: Keep core functionality minimal while enabling rich features through plugins
|
|
40
|
-
- **Reusability**: Share common functionality across multiple CLI applications
|
|
41
|
-
- **Type Safety**: Full TypeScript support with type inference for extensions
|
|
42
|
-
- **Ecosystem**: Build and share plugins with the community
|
|
43
|
-
|
|
44
|
-
### Package vs Entry Point
|
|
45
|
-
|
|
46
|
-
This package (`@gunshi/plugin`) contains the same exports as the `gunshi/plugin` entry point in the main gunshi package. Choose based on your needs:
|
|
47
|
-
|
|
48
|
-
- Use `@gunshi/plugin` when developing standalone plugin packages to minimize dependencies
|
|
49
|
-
- Use `gunshi/plugin` when plugins are part of your CLI application
|
|
50
|
-
|
|
51
|
-
## 🌟 Features
|
|
52
|
-
|
|
53
|
-
Plugins can extend gunshi in multiple ways:
|
|
54
|
-
|
|
55
|
-
### Extend CommandContext
|
|
56
|
-
|
|
57
|
-
Add custom properties to the command execution context that are available in all commands.
|
|
58
|
-
|
|
59
|
-
<!-- eslint-skip -->
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
// Plugin adds authentication info to context
|
|
63
|
-
ctx.extensions['auth'].user // { id: '123', name: 'John' }
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Decorate Renderers
|
|
67
|
-
|
|
68
|
-
Customize how help messages, usage information, and validation errors are displayed.
|
|
69
|
-
|
|
70
|
-
```ts
|
|
71
|
-
// Add colors, icons, or custom formatting
|
|
72
|
-
decorateHeaderRenderer((base, ctx) => `🚀 ${base(ctx)}`)
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Wrap Command Execution
|
|
76
|
-
|
|
77
|
-
Add pre/post processing logic around command execution.
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
// Add logging, timing, error handling
|
|
81
|
-
decorateCommand(runner => async ctx => {
|
|
82
|
-
console.log('Before command')
|
|
83
|
-
const result = await runner(ctx)
|
|
84
|
-
console.log('After command')
|
|
85
|
-
return result
|
|
86
|
-
})
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Add Global Options
|
|
90
|
-
|
|
91
|
-
Register options available to all commands.
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
// Add --verbose, --debug, etc.
|
|
95
|
-
addGlobalOption('verbose', { type: 'boolean', alias: 'v' })
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Add Sub Commands
|
|
99
|
-
|
|
100
|
-
Dynamically register new commands.
|
|
101
|
-
|
|
102
|
-
```ts
|
|
103
|
-
// Add utility commands like 'complete', 'config', etc.
|
|
104
|
-
addCommand('config', configCommand)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Manage Plugin Dependencies
|
|
108
|
-
|
|
109
|
-
Declare dependencies between plugins with automatic resolution.
|
|
110
|
-
|
|
111
|
-
<!-- eslint-skip -->
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
// Ensure required plugins are loaded first
|
|
115
|
-
dependencies: ['logger', { id: 'cache', optional: true }]
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## 💿 Installation
|
|
119
|
-
|
|
120
|
-
```sh
|
|
121
|
-
# npm
|
|
122
|
-
npm install --save @gunshi/plugin
|
|
123
|
-
|
|
124
|
-
# pnpm
|
|
125
|
-
pnpm add @gunshi/plugin
|
|
126
|
-
|
|
127
|
-
# yarn
|
|
128
|
-
yarn add @gunshi/plugin
|
|
129
|
-
|
|
130
|
-
# deno
|
|
131
|
-
deno add jsr:@gunshi/plugin
|
|
132
|
-
|
|
133
|
-
# bun
|
|
134
|
-
bun add @gunshi/plugin
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## 🚀 Quick Start
|
|
138
|
-
|
|
139
|
-
### Minimal Plugin (No Extension)
|
|
140
|
-
|
|
141
|
-
```ts
|
|
142
|
-
import { plugin } from '@gunshi/plugin'
|
|
143
|
-
|
|
144
|
-
export default plugin({
|
|
145
|
-
id: 'my-plugin',
|
|
146
|
-
name: 'My First Plugin',
|
|
147
|
-
|
|
148
|
-
setup(ctx) {
|
|
149
|
-
console.log('Plugin loaded!')
|
|
150
|
-
|
|
151
|
-
// Add a global --quiet option
|
|
152
|
-
ctx.addGlobalOption('quiet', {
|
|
153
|
-
type: 'boolean',
|
|
154
|
-
alias: 'q',
|
|
155
|
-
description: 'Suppress output'
|
|
156
|
-
})
|
|
157
|
-
}
|
|
158
|
-
})
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Plugin with Extension
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
import { plugin } from '@gunshi/plugin'
|
|
165
|
-
|
|
166
|
-
export interface LoggerExtension {
|
|
167
|
-
log: (message: string) => void
|
|
168
|
-
error: (message: string) => void
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export default plugin({
|
|
172
|
-
id: 'logger',
|
|
173
|
-
name: 'Logger Plugin',
|
|
174
|
-
|
|
175
|
-
// Called for each command execution
|
|
176
|
-
extension: (ctx, cmd) => {
|
|
177
|
-
const quiet = ctx.values.quiet ?? false
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
log: (message: string) => {
|
|
181
|
-
if (!quiet) {
|
|
182
|
-
console.log(message)
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
error: (message: string) => {
|
|
186
|
-
console.error(message)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
|
|
191
|
-
// Called after extension is applied
|
|
192
|
-
onExtension: (ctx, cmd) => {
|
|
193
|
-
ctx.extensions.logger.log('Logger initialized')
|
|
194
|
-
}
|
|
195
|
-
})
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## 🔄 Gunshi Lifecycle and Plugins
|
|
199
|
-
|
|
200
|
-
### CLI Execution Lifecycle
|
|
201
|
-
|
|
202
|
-
```mermaid
|
|
203
|
-
graph TD
|
|
204
|
-
A[A. CLI Start] --> B[B. Load Plugins]
|
|
205
|
-
B --> C[C. Resolve Dependencies]
|
|
206
|
-
C --> D[D. Execute Plugin Setup]
|
|
207
|
-
D --> E[E. Parse Arguments]
|
|
208
|
-
E --> F[F. Resolve Command]
|
|
209
|
-
F --> G[G. Resolve & Validate Args]
|
|
210
|
-
G --> H[H. Create CommandContext]
|
|
211
|
-
H --> I[I. Apply Extensions]
|
|
212
|
-
I --> J[J. Execute onExtension]
|
|
213
|
-
J --> K[K. Execute Command]
|
|
214
|
-
K --> L[L. CLI End]
|
|
215
|
-
|
|
216
|
-
style B fill:#468c56,color:white
|
|
217
|
-
style C fill:#468c56,color:white
|
|
218
|
-
style D fill:#468c56,color:white
|
|
219
|
-
style I fill:#468c56,color:white
|
|
220
|
-
style J fill:#468c56,color:white
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Plugin Interaction Points
|
|
224
|
-
|
|
225
|
-
Plugins interact with gunshi at two distinct phases during the CLI lifecycle:
|
|
226
|
-
|
|
227
|
-
**Setup Phase** (steps B-D in the lifecycle):
|
|
228
|
-
|
|
229
|
-
- Occurs during plugin initialization
|
|
230
|
-
- Plugins configure the CLI by adding options, commands, and decorators
|
|
231
|
-
- All modifications are registered but not yet executed
|
|
232
|
-
- This is when the `setup()` function runs
|
|
233
|
-
|
|
234
|
-
**Execution Phase** (steps I-K in the lifecycle):
|
|
235
|
-
|
|
236
|
-
- Occurs when a command is actually being executed
|
|
237
|
-
- Extensions are created and applied to CommandContext
|
|
238
|
-
- Decorators are executed to modify behavior
|
|
239
|
-
- This is when `extension()` and `onExtension()` run
|
|
240
|
-
|
|
241
|
-
The following diagram shows how setup configurations connect to execution behaviors:
|
|
242
|
-
|
|
243
|
-
```mermaid
|
|
244
|
-
graph LR
|
|
245
|
-
subgraph "Setup Phase"
|
|
246
|
-
S1[addGlobalOption<br/>Register global options]
|
|
247
|
-
S2[addCommand<br/>Register commands]
|
|
248
|
-
S3[decorateHeaderRenderer<br/>Customize header]
|
|
249
|
-
S4[decorateUsageRenderer<br/>Customize usage]
|
|
250
|
-
S5[decorateValidationErrorsRenderer<br/>Customize errors]
|
|
251
|
-
S6[decorateCommand<br/>Wrap execution]
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
subgraph "Execution Phase"
|
|
255
|
-
E1[extension factory<br/>Create context extension]
|
|
256
|
-
E2[onExtension callback<br/>Post-extension hook]
|
|
257
|
-
E3[Decorated renderers<br/>Custom rendering]
|
|
258
|
-
E4[Decorated command<br/>Wrapped execution]
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
S1 --> E3
|
|
262
|
-
S2 --> E4
|
|
263
|
-
S3 --> E3
|
|
264
|
-
S4 --> E3
|
|
265
|
-
S5 --> E3
|
|
266
|
-
S6 --> E4
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## 🔧 Plugin in Depth
|
|
270
|
-
|
|
271
|
-
### Plugin Dependency Resolution
|
|
272
|
-
|
|
273
|
-
Gunshi uses **topological sorting** to resolve plugin dependencies, ensuring plugins are loaded in the correct order.
|
|
274
|
-
|
|
275
|
-
#### Resolution Process
|
|
276
|
-
|
|
277
|
-
1. **Build Dependency Graph**: Create a directed graph of plugin dependencies
|
|
278
|
-
2. **Detect Cycles**: Check for circular dependencies (throws error if found)
|
|
279
|
-
3. **Topological Sort**: Order plugins so dependencies load before dependents
|
|
280
|
-
4. **Load Plugins**: Execute setup in resolved order
|
|
281
|
-
|
|
282
|
-
Example:
|
|
283
|
-
|
|
284
|
-
```ts
|
|
285
|
-
// Given these plugins:
|
|
286
|
-
const pluginA = plugin({
|
|
287
|
-
id: 'a',
|
|
288
|
-
dependencies: ['b', 'c']
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
const pluginB = plugin({
|
|
292
|
-
id: 'b',
|
|
293
|
-
dependencies: ['d']
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
const pluginC = plugin({
|
|
297
|
-
id: 'c'
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
const pluginD = plugin({
|
|
301
|
-
id: 'd'
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
// Resolution order: d → b → c → a
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
#### Optional Dependencies
|
|
308
|
-
|
|
309
|
-
```ts
|
|
310
|
-
plugin({
|
|
311
|
-
id: 'logger',
|
|
312
|
-
dependencies: [
|
|
313
|
-
'core', // Required: throws if missing
|
|
314
|
-
{ id: 'colors', optional: true } // Optional: continues if missing
|
|
315
|
-
],
|
|
316
|
-
|
|
317
|
-
setup(ctx) {
|
|
318
|
-
// Check if optional dependency loaded
|
|
319
|
-
if (ctx.hasCommand('colors')) {
|
|
320
|
-
// Use colors functionality
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
})
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Decorators
|
|
327
|
-
|
|
328
|
-
Decorators in gunshi follow the **LIFO (Last In, First Out)** pattern.
|
|
329
|
-
|
|
330
|
-
#### Execution Order
|
|
331
|
-
|
|
332
|
-
```ts
|
|
333
|
-
// Registration order
|
|
334
|
-
ctx.decorateCommand(decoratorA) // Registered first
|
|
335
|
-
ctx.decorateCommand(decoratorB) // Registered second
|
|
336
|
-
ctx.decorateCommand(decoratorC) // Registered third
|
|
337
|
-
|
|
338
|
-
// Execution order: C → B → A → original
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
#### Renderer Decorators
|
|
342
|
-
|
|
343
|
-
Renderer decorators allow you to customize how gunshi displays various output elements like headers, usage information, and error messages. They wrap the base renderer functions to modify or enhance their output.
|
|
344
|
-
|
|
345
|
-
**Common Use Cases:**
|
|
346
|
-
|
|
347
|
-
- Adding colors or styling to output
|
|
348
|
-
- Wrapping content in boxes or borders
|
|
349
|
-
- Adding additional help information
|
|
350
|
-
- Translating or localizing messages
|
|
351
|
-
- Formatting errors for better readability
|
|
352
|
-
|
|
353
|
-
```ts
|
|
354
|
-
plugin({
|
|
355
|
-
setup(ctx) {
|
|
356
|
-
// Header decorator - Customize command header display
|
|
357
|
-
ctx.decorateHeaderRenderer(async (baseRenderer, ctx) => {
|
|
358
|
-
const header = await baseRenderer(ctx)
|
|
359
|
-
// Add a decorative box around the header
|
|
360
|
-
return `╭─────────────────╮\n│ ${header} │\n╰─────────────────╯`
|
|
361
|
-
})
|
|
362
|
-
|
|
363
|
-
// Usage decorator - Enhance help message
|
|
364
|
-
ctx.decorateUsageRenderer(async (baseRenderer, ctx) => {
|
|
365
|
-
const usage = await baseRenderer(ctx)
|
|
366
|
-
// Append additional help resources
|
|
367
|
-
return usage + '\n\nFor more help: https://docs.example.com'
|
|
368
|
-
})
|
|
369
|
-
|
|
370
|
-
// Validation errors decorator - Format error display
|
|
371
|
-
ctx.decorateValidationErrorsRenderer(async (baseRenderer, ctx, error) => {
|
|
372
|
-
const errors = await baseRenderer(ctx, error)
|
|
373
|
-
// Add error icon and formatting
|
|
374
|
-
return `❌ Validation Failed\n\n${errors}`
|
|
375
|
-
})
|
|
376
|
-
}
|
|
377
|
-
})
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
**Example Output:**
|
|
381
|
-
|
|
382
|
-
```sh
|
|
383
|
-
# Before decoration (plain):
|
|
384
|
-
my-cli deploy - Deploy application
|
|
385
|
-
|
|
386
|
-
# After header decoration:
|
|
387
|
-
╭─────────────────╮
|
|
388
|
-
│ my-cli deploy - Deploy application │
|
|
389
|
-
╰─────────────────╯
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
#### Command Decorators
|
|
393
|
-
|
|
394
|
-
Command decorators wrap the actual command execution, allowing you to add behavior before and after commands run. They're perfect for cross-cutting concerns that apply to all commands.
|
|
395
|
-
|
|
396
|
-
**Common Use Cases:**
|
|
397
|
-
|
|
398
|
-
- Performance monitoring and timing
|
|
399
|
-
- Logging and auditing
|
|
400
|
-
- Error handling and recovery
|
|
401
|
-
- Authentication checks
|
|
402
|
-
- Transaction management
|
|
403
|
-
- Resource cleanup
|
|
404
|
-
|
|
405
|
-
```ts
|
|
406
|
-
plugin({
|
|
407
|
-
setup(ctx) {
|
|
408
|
-
// Timing decorator - Measure execution time
|
|
409
|
-
ctx.decorateCommand(runner => async ctx => {
|
|
410
|
-
const start = performance.now()
|
|
411
|
-
console.log(`🚀 Starting command: ${ctx.name || 'root'}`)
|
|
412
|
-
|
|
413
|
-
try {
|
|
414
|
-
const result = await runner(ctx)
|
|
415
|
-
const duration = performance.now() - start
|
|
416
|
-
console.log(`✅ Completed in ${duration.toFixed(2)}ms`)
|
|
417
|
-
return result
|
|
418
|
-
} catch (error) {
|
|
419
|
-
const duration = performance.now() - start
|
|
420
|
-
console.log(`❌ Failed after ${duration.toFixed(2)}ms`)
|
|
421
|
-
throw error
|
|
422
|
-
}
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
// Error handling decorator - Enhance error reporting
|
|
426
|
-
ctx.decorateCommand(runner => async ctx => {
|
|
427
|
-
try {
|
|
428
|
-
return await runner(ctx)
|
|
429
|
-
} catch (error) {
|
|
430
|
-
// Log detailed error information
|
|
431
|
-
console.error('Command failed:', error.message)
|
|
432
|
-
console.error('Context:', {
|
|
433
|
-
command: ctx.name,
|
|
434
|
-
args: ctx.values,
|
|
435
|
-
positionals: ctx.positionals
|
|
436
|
-
})
|
|
437
|
-
|
|
438
|
-
// Could also send to error tracking service
|
|
439
|
-
// await errorTracker.report(error, ctx)
|
|
440
|
-
|
|
441
|
-
throw error
|
|
442
|
-
}
|
|
443
|
-
})
|
|
444
|
-
|
|
445
|
-
// Authentication decorator - Ensure user is authenticated
|
|
446
|
-
ctx.decorateCommand(runner => async ctx => {
|
|
447
|
-
const auth = ctx.extensions.auth
|
|
448
|
-
|
|
449
|
-
if (auth && !auth.isAuthenticated()) {
|
|
450
|
-
throw new Error('Authentication required. Please login first.')
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return await runner(ctx)
|
|
454
|
-
})
|
|
455
|
-
}
|
|
456
|
-
})
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
**Execution Flow Example:**
|
|
460
|
-
|
|
461
|
-
When multiple decorators are applied, they execute in LIFO order:
|
|
462
|
-
|
|
463
|
-
```sh
|
|
464
|
-
User runs command
|
|
465
|
-
→ Authentication decorator (last registered)
|
|
466
|
-
→ Error handling decorator
|
|
467
|
-
→ Timing decorator (first registered)
|
|
468
|
-
→ Actual command execution
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
## 🤝 Plugin Communication & Collaboration
|
|
472
|
-
|
|
473
|
-
Gunshi's plugin system provides strong type safety for plugin interactions through TypeScript's type system. This section explains how to create type-safe plugin ecosystems where plugins can communicate and share functionality.
|
|
474
|
-
|
|
475
|
-
### Type-Safe Access to Other Plugin Extensions
|
|
476
|
-
|
|
477
|
-
The `plugin` function supports type parameters that allow you to declare and access other plugin extensions with full type safety:
|
|
478
|
-
|
|
479
|
-
```ts
|
|
480
|
-
import { plugin } from '@gunshi/plugin'
|
|
481
|
-
|
|
482
|
-
// Define extension interfaces for each plugin
|
|
483
|
-
export interface LoggerExtension {
|
|
484
|
-
log: (message: string) => void
|
|
485
|
-
error: (message: string) => void
|
|
486
|
-
debug: (message: string) => void
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
export interface AuthExtension {
|
|
490
|
-
isAuthenticated: () => boolean
|
|
491
|
-
getUser: () => { id: string; name: string }
|
|
492
|
-
getToken: () => string | undefined
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Logger plugin - provides LoggerExtension
|
|
496
|
-
export const loggerPlugin = plugin({
|
|
497
|
-
id: 'logger',
|
|
498
|
-
name: 'Logger Plugin',
|
|
499
|
-
|
|
500
|
-
extension: (): LoggerExtension => ({
|
|
501
|
-
log: (message: string) => console.log(`[LOG] ${message}`),
|
|
502
|
-
error: (message: string) => console.error(`[ERROR] ${message}`),
|
|
503
|
-
debug: (message: string) => console.debug(`[DEBUG] ${message}`)
|
|
504
|
-
})
|
|
505
|
-
})
|
|
506
|
-
|
|
507
|
-
// Auth plugin - provides AuthExtension and uses LoggerExtension
|
|
508
|
-
export const authPlugin = plugin<
|
|
509
|
-
{ logger: LoggerExtension }, // Declare dependency extensions
|
|
510
|
-
'auth', // Plugin ID as literal type
|
|
511
|
-
['logger'], // Plugin Dependencies type
|
|
512
|
-
AuthExtensions // Extension factory return type
|
|
513
|
-
>({
|
|
514
|
-
id: 'auth',
|
|
515
|
-
name: 'Authentication Plugin',
|
|
516
|
-
dependencies: ['logger'], // dependency declaration
|
|
517
|
-
|
|
518
|
-
setup: ctx => {
|
|
519
|
-
// Type-safe access to logger in setup
|
|
520
|
-
ctx.decorateCommand(runner => async cmdCtx => {
|
|
521
|
-
// cmdCtx.extensions is typed as { auth: AuthExtension, logger: LoggerExtension }
|
|
522
|
-
cmdCtx.extensions.logger.log('Command started')
|
|
523
|
-
return await runner(cmdCtx)
|
|
524
|
-
})
|
|
525
|
-
},
|
|
526
|
-
|
|
527
|
-
extension: ctx => {
|
|
528
|
-
// ctx.extensions.logger is fully typed as LoggerExtension
|
|
529
|
-
ctx.extensions.logger.log('Auth plugin initializing')
|
|
530
|
-
|
|
531
|
-
return {
|
|
532
|
-
isAuthenticated: () => {
|
|
533
|
-
const token = process.env.AUTH_TOKEN
|
|
534
|
-
return Boolean(token)
|
|
535
|
-
},
|
|
536
|
-
getUser: () => ({
|
|
537
|
-
id: '123',
|
|
538
|
-
name: 'John Doe'
|
|
539
|
-
}),
|
|
540
|
-
getToken: () => process.env.AUTH_TOKEN
|
|
541
|
-
}
|
|
542
|
-
},
|
|
543
|
-
|
|
544
|
-
onExtension: ctx => {
|
|
545
|
-
// Both extensions are available and typed
|
|
546
|
-
if (ctx.extensions.auth.isAuthenticated()) {
|
|
547
|
-
ctx.extensions.logger.log('User authenticated')
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
})
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
### Type-Safe Plugin Extensions in Commands
|
|
554
|
-
|
|
555
|
-
When defining commands, you can specify which plugin extensions your command requires, and TypeScript will ensure type safety:
|
|
556
|
-
|
|
557
|
-
```ts
|
|
558
|
-
import { define, CommandContext } from 'gunshi'
|
|
559
|
-
import type { LoggerExtension, AuthExtension, DatabaseExtension } from 'your-plugin'
|
|
560
|
-
|
|
561
|
-
// Define the extensions your command needs
|
|
562
|
-
type MyCommandExtensions = {
|
|
563
|
-
logger: LoggerExtension
|
|
564
|
-
auth: AuthExtension
|
|
565
|
-
db?: DatabaseExtension // Optional extension
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// Define command with typed extensions
|
|
569
|
-
export const deployCommand = define<MyCommandExtensions>({
|
|
570
|
-
name: 'deploy',
|
|
571
|
-
description: 'Deploy the application',
|
|
572
|
-
run: async ctx => {
|
|
573
|
-
// All extensions are fully typed
|
|
574
|
-
const { logger, auth, db } = ctx.extensions
|
|
575
|
-
|
|
576
|
-
// Type-safe usage
|
|
577
|
-
if (!auth.isAuthenticated()) {
|
|
578
|
-
logger.error('Authentication required')
|
|
579
|
-
throw new Error('Please login first')
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const user = auth.getUser()
|
|
583
|
-
logger.log(`Deploying as ${user.name}`)
|
|
584
|
-
|
|
585
|
-
// Optional extension with safe access
|
|
586
|
-
await db?.logDeployment(user.id, ctx.values.environment)
|
|
587
|
-
|
|
588
|
-
// Command implementation...
|
|
589
|
-
}
|
|
590
|
-
})
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
## 💡 Plugin Best Practices
|
|
594
|
-
|
|
595
|
-
### Plugin Package Naming Conventions
|
|
596
|
-
|
|
597
|
-
Follow these naming conventions for consistency:
|
|
598
|
-
|
|
599
|
-
- **Standalone packages**: `gunshi-plugin-{feature}`
|
|
600
|
-
- Example: `gunshi-plugin-logger`, `gunshi-plugin-auth`
|
|
601
|
-
- **Scoped packages**: `@{scope}/plugin-{feature}`
|
|
602
|
-
- Example: `@mycompany/plugin-logger`
|
|
603
|
-
|
|
604
|
-
### Plugin ID Namespacing & Definition
|
|
605
|
-
|
|
606
|
-
Use namespaced IDs to avoid conflicts and export them for reusability:
|
|
607
|
-
|
|
608
|
-
```ts
|
|
609
|
-
// ✅ Good: namespaced
|
|
610
|
-
plugin({ id: 'mycompany:auth' })
|
|
611
|
-
plugin({ id: 'g:renderer' }) // 'g:' for gunshi official
|
|
612
|
-
|
|
613
|
-
// ❌ Bad: might conflict
|
|
614
|
-
plugin({ id: 'auth' })
|
|
615
|
-
plugin({ id: 'logger' })
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
**Export your plugin ID to prevent hardcoding:**
|
|
619
|
-
|
|
620
|
-
By exporting your plugin ID, you enable other plugin authors and users to reference it without hardcoding strings:
|
|
621
|
-
|
|
622
|
-
<!-- eslint-skip -->
|
|
623
|
-
|
|
624
|
-
```ts
|
|
625
|
-
// Export plugin ID as a constant
|
|
626
|
-
export const pluginId = 'mycompany:auth' as const
|
|
627
|
-
|
|
628
|
-
// Export plugin ID type for `plugin` function type parameters
|
|
629
|
-
export type PluginId = typeof pluginId
|
|
630
|
-
|
|
631
|
-
// your-plugin/src/index.ts
|
|
632
|
-
import { pluginId } from './types.ts'
|
|
633
|
-
|
|
634
|
-
export { pluginId } from './types.ts' // Re-export for consumers
|
|
635
|
-
|
|
636
|
-
export default function auth() {
|
|
637
|
-
return plugin({
|
|
638
|
-
id: pluginId // Use the constant instead of hardcoding
|
|
639
|
-
// ...
|
|
640
|
-
})
|
|
641
|
-
}
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
**Benefits for other plugin authors:**
|
|
645
|
-
|
|
646
|
-
Other plugins can now import and use your plugin ID directly:
|
|
647
|
-
|
|
648
|
-
```ts
|
|
649
|
-
// another-plugin/src/index.ts
|
|
650
|
-
import { pluginId as authPluginId } from 'your-auth-plugin'
|
|
651
|
-
import type { PluginId as AuthId, AuthExtension } from 'your-auth-plugin'
|
|
652
|
-
|
|
653
|
-
export const pluginId = 'mycompany:api' as const
|
|
654
|
-
export type PluginId = typeof pluginId
|
|
655
|
-
|
|
656
|
-
// Define plugin extension for user ...
|
|
657
|
-
export interface ApiExtension {
|
|
658
|
-
// ...
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// Use imported ID instead of hardcoding 'mycompany:auth'
|
|
662
|
-
const dependencies = [authPluginId] as const
|
|
663
|
-
|
|
664
|
-
export default function api() {
|
|
665
|
-
// Set Auth Plugin Extension to type parameters to use it as typed extensions
|
|
666
|
-
return plugin<Record<AuthId, AuthExtension>, PluginId, typeof dependencies, ApiExtension>({
|
|
667
|
-
id: pluginId,
|
|
668
|
-
dependencies,
|
|
669
|
-
|
|
670
|
-
onExtension: ctx => {
|
|
671
|
-
// Access extension using imported ID
|
|
672
|
-
const auth = ctx.extensions[authPluginId]
|
|
673
|
-
|
|
674
|
-
if (auth.isAuthenticated()) {
|
|
675
|
-
// Use auth features
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
})
|
|
679
|
-
}
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
**Benefits for plugin users:**
|
|
683
|
-
|
|
684
|
-
Users can also access extensions without hardcoding plugin IDs:
|
|
685
|
-
|
|
686
|
-
```ts
|
|
687
|
-
// user-code.ts
|
|
688
|
-
import { define } from 'gunshi'
|
|
689
|
-
import auth, { pluginId as authId } from 'your-auth-plugin'
|
|
690
|
-
import type { AuthExtension, PluginId as AuthId } from 'your-auth-plugin'
|
|
691
|
-
|
|
692
|
-
const myCommand = define<Record<AuthId, AuthExtension>>({
|
|
693
|
-
name: 'deploy',
|
|
694
|
-
|
|
695
|
-
run: ctx => {
|
|
696
|
-
// Type-safe access using imported plugin id, no hardcoded 'mycompany:auth' strings!
|
|
697
|
-
const auth = ctx.extensions[authId]
|
|
698
|
-
|
|
699
|
-
if (!auth.isAuthenticated()) {
|
|
700
|
-
throw new Error('Please login first')
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
})
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
**Complete example with multiple plugins:**
|
|
707
|
-
|
|
708
|
-
```ts
|
|
709
|
-
// Export all plugin IDs from a central location
|
|
710
|
-
// plugins/index.ts
|
|
711
|
-
export { pluginId as authPluginId } from '@mycompany/plugin-auth'
|
|
712
|
-
export { pluginId as loggerPluginId } from '@mycompany/plugin-logger'
|
|
713
|
-
export { pluginId as dbPluginId } from '@mycompany/plugin-database'
|
|
714
|
-
|
|
715
|
-
// Use them consistently across your application
|
|
716
|
-
import { cli } from 'gunshi'
|
|
717
|
-
import auth, { pluginId as authId } from '@mycompany/plugin-auth'
|
|
718
|
-
import logger, { pluginId as loggerId } from '@mycompany/plugin-logger'
|
|
719
|
-
import db, { pluginId as dbId } from '@mycompany/plugin-database'
|
|
720
|
-
|
|
721
|
-
// Import extension types
|
|
722
|
-
import type { PluginId as AuthId, AuthExtension } from '@mycompany/plugin-auth'
|
|
723
|
-
import type { PluginId as LoggerId, LoggerExtension } from '@mycompany/plugin-logger'
|
|
724
|
-
import type { PluginId as DbId, DbExtension } from '@mycompany/plugin-database'
|
|
725
|
-
|
|
726
|
-
// Define extensions to use it for command context as typed extensions
|
|
727
|
-
type Extensions = Record<AuthId, AuthExtension> &
|
|
728
|
-
Record<LoggerId, LoggerExtension> &
|
|
729
|
-
Record<DbId, DbExtension>
|
|
730
|
-
|
|
731
|
-
await cli<Extensions>(
|
|
732
|
-
process.argv.slice(2),
|
|
733
|
-
// Entry command
|
|
734
|
-
async ctx => {
|
|
735
|
-
// All plugin IDs are imported, no hardcoding needed
|
|
736
|
-
const auth = ctx.extensions[authId]
|
|
737
|
-
const logger = ctx.extensions[loggerId]
|
|
738
|
-
const db = ctx.extensions[dbId]
|
|
739
|
-
|
|
740
|
-
logger.log('Application started')
|
|
741
|
-
|
|
742
|
-
if (auth.isAuthenticated()) {
|
|
743
|
-
await db.connect()
|
|
744
|
-
}
|
|
745
|
-
},
|
|
746
|
-
// CLI options
|
|
747
|
-
{
|
|
748
|
-
plugins: [auth(), logger(), db()] // Install plugins
|
|
749
|
-
}
|
|
750
|
-
)
|
|
751
|
-
```
|
|
752
|
-
|
|
753
|
-
### Declare Plugin Dependencies Explicitly
|
|
754
|
-
|
|
755
|
-
Always declare your plugin dependencies explicitly to ensure proper initialization order and availability:
|
|
756
|
-
|
|
757
|
-
<!-- eslint-skip -->
|
|
758
|
-
|
|
759
|
-
```ts
|
|
760
|
-
dependencies: ['required-plugin', { id: 'optional-plugin', optional: true }]
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
**Why explicit dependencies are important:**
|
|
764
|
-
|
|
765
|
-
1. **Guaranteed Load Order**: Gunshi uses topological sorting to ensure dependencies are loaded before dependent plugins
|
|
766
|
-
2. **Runtime Safety**: Required dependencies throw errors if missing, preventing runtime failures
|
|
767
|
-
3. **Optional Flexibility**: Optional dependencies allow graceful degradation when plugins aren't available
|
|
768
|
-
4. **Clear Documentation**: Dependencies are self-documenting, making it clear what plugins work together
|
|
769
|
-
5. **Type Safety**: When combined with TypeScript, dependencies enable proper type checking of extensions
|
|
770
|
-
|
|
771
|
-
**Example with reasoning:**
|
|
772
|
-
|
|
773
|
-
```ts
|
|
774
|
-
export default function analytics() {
|
|
775
|
-
return plugin({
|
|
776
|
-
id: 'mycompany:analytics',
|
|
777
|
-
|
|
778
|
-
// Declare dependencies explicitly
|
|
779
|
-
dependencies: [
|
|
780
|
-
'mycompany:logger', // Required: analytics needs logging
|
|
781
|
-
'mycompany:auth', // Required: need user info for tracking
|
|
782
|
-
{ id: 'mycompany:database', optional: true } // Optional: can work without persistence
|
|
783
|
-
],
|
|
784
|
-
|
|
785
|
-
extension: ctx => {
|
|
786
|
-
// Required dependencies are guaranteed to exist
|
|
787
|
-
const logger = ctx.extensions['mycompany:logger'] // Safe to access
|
|
788
|
-
const auth = ctx.extensions['mycompany:auth'] // Safe to access
|
|
789
|
-
|
|
790
|
-
// Optional dependencies might be undefined
|
|
791
|
-
const db = ctx.extensions['mycompany:db'] // May be undefined
|
|
792
|
-
|
|
793
|
-
return {
|
|
794
|
-
track: async (event: string, data: unknown) => {
|
|
795
|
-
const user = auth.getUser()
|
|
796
|
-
logger.log(`Track: ${event} by ${user.id}`)
|
|
797
|
-
|
|
798
|
-
// Gracefully handle optional dependency
|
|
799
|
-
if (db) {
|
|
800
|
-
await db.store('analytics', { event, user, data })
|
|
801
|
-
} else {
|
|
802
|
-
logger.warn('Analytics not persisted (database plugin not available)')
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
})
|
|
808
|
-
}
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
**Benefits of explicit dependencies:**
|
|
812
|
-
|
|
813
|
-
- **Fail Fast**: Missing required plugins fail at startup, not runtime
|
|
814
|
-
- **Predictable**: Plugin loading order is deterministic
|
|
815
|
-
- **Maintainable**: Easy to see and update plugin relationships
|
|
816
|
-
- **Testable**: Can mock dependencies in tests
|
|
817
|
-
|
|
818
|
-
### Avoid Circular Dependencies
|
|
819
|
-
|
|
820
|
-
<!-- eslint-skip -->
|
|
821
|
-
|
|
822
|
-
```ts
|
|
823
|
-
// ❌ Bad: Circular dependency
|
|
824
|
-
pluginA: {
|
|
825
|
-
dependencies: ['pluginB']
|
|
826
|
-
}
|
|
827
|
-
pluginB: {
|
|
828
|
-
dependencies: ['pluginA']
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
// ✅ Good: Extract shared functionality
|
|
832
|
-
pluginShared: {
|
|
833
|
-
/* shared logic */
|
|
834
|
-
}
|
|
835
|
-
pluginA: {
|
|
836
|
-
dependencies: ['pluginShared']
|
|
837
|
-
}
|
|
838
|
-
pluginB: {
|
|
839
|
-
dependencies: ['pluginShared']
|
|
840
|
-
}
|
|
841
|
-
```
|
|
842
|
-
|
|
843
|
-
### Gracefully Handle Optional Dependencies
|
|
844
|
-
|
|
845
|
-
When declaring optional dependencies, your plugin must be designed to work correctly even when those dependencies are not installed. This ensures your plugin can adapt to different environments and configurations.
|
|
846
|
-
|
|
847
|
-
**Key principles:**
|
|
848
|
-
|
|
849
|
-
1. **Always check for existence** before using optional dependencies
|
|
850
|
-
2. **Provide fallback behavior** when optional dependencies are unavailable
|
|
851
|
-
3. **Don't assume optional dependencies exist** in any part of your code
|
|
852
|
-
4. **Test your plugin** with and without optional dependencies
|
|
853
|
-
|
|
854
|
-
**Example implementation pattern:**
|
|
855
|
-
|
|
856
|
-
```ts
|
|
857
|
-
import { pluginId as i18nPluginId, resolveKey } from '@gunshi/plugin-i18n'
|
|
858
|
-
import type { I18nExtension } from '@gunshi/plugin-i18n'
|
|
859
|
-
|
|
860
|
-
export const pluginId = 'g:renderer' as const
|
|
861
|
-
|
|
862
|
-
export interface UsageRendererExtension {
|
|
863
|
-
// ...
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
const dependencies = [i18nPluginId] as const
|
|
867
|
-
|
|
868
|
-
export default function renderer() {
|
|
869
|
-
return plugin<
|
|
870
|
-
Record<typeof i18nPluginId, I18nExtension>,
|
|
871
|
-
typeof pluginId,
|
|
872
|
-
typeof dependencies,
|
|
873
|
-
UsageRendererExtension
|
|
874
|
-
>({
|
|
875
|
-
id: pluginId,
|
|
876
|
-
name: 'Usage Renderer',
|
|
877
|
-
dependencies,
|
|
878
|
-
|
|
879
|
-
extension: (ctx, cmd) => {
|
|
880
|
-
// Get optional extension (may be undefined)
|
|
881
|
-
const i18n = ctx.extensions[i18nPluginId]
|
|
882
|
-
|
|
883
|
-
return {
|
|
884
|
-
// Gracefully handle missing i18n plugin
|
|
885
|
-
text: localizable(ctx, cmd, i18n?.translate), // Pass undefined if i18n not available
|
|
886
|
-
|
|
887
|
-
renderHelp: ctx => {
|
|
888
|
-
// eslint-disable-next-line unicorn/prefer-ternary -- example
|
|
889
|
-
if (i18n) {
|
|
890
|
-
// Use i18n features when available
|
|
891
|
-
return i18n.translate(resolveKey('message', ctx))
|
|
892
|
-
} else {
|
|
893
|
-
// Fallback to default behavior
|
|
894
|
-
return 'Help message'
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
},
|
|
899
|
-
|
|
900
|
-
onExtension: async (ctx, cmd) => {
|
|
901
|
-
const i18n = ctx.extensions[i18nPluginId]
|
|
902
|
-
|
|
903
|
-
// Only use optional features if available
|
|
904
|
-
if (i18n) {
|
|
905
|
-
await i18n.loadResource(ctx.locale, ctx, cmd)
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// Core functionality works regardless
|
|
909
|
-
console.log('Renderer plugin initialized')
|
|
910
|
-
}
|
|
911
|
-
})
|
|
912
|
-
}
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
**Implementing adaptive functionality:**
|
|
916
|
-
|
|
917
|
-
```ts
|
|
918
|
-
// Helper function that works with or without optional dependency
|
|
919
|
-
function localizable(ctx: CommandContext, cmd: Command, translate?: TranslateFunction) {
|
|
920
|
-
return (key: string, values?: Record<string, unknown>) => {
|
|
921
|
-
// eslint-disable-next-line unicorn/prefer-ternary -- example
|
|
922
|
-
if (translate) {
|
|
923
|
-
// Use translation when available
|
|
924
|
-
return translate(key, values)
|
|
925
|
-
} else {
|
|
926
|
-
// Fallback to key or default resource
|
|
927
|
-
return DefaultResource[key] || key
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
**Benefits of this approach:**
|
|
934
|
-
|
|
935
|
-
- **Flexibility**: Users can choose which plugins to install
|
|
936
|
-
- **Reduced dependencies**: Core functionality doesn't require all plugins
|
|
937
|
-
- **Better performance**: Smaller installation size when optional plugins are omitted
|
|
938
|
-
- **Graceful degradation**: Features degrade smoothly rather than failing completely
|
|
939
|
-
|
|
940
|
-
### Use extensions with optional chaining
|
|
941
|
-
|
|
942
|
-
Plugin extensions are dynamically injected, so it is safe to use optional chaining in the following cases.
|
|
943
|
-
|
|
944
|
-
#### Optional plugin dependencies
|
|
945
|
-
|
|
946
|
-
If your plugin depends on a specific plugin extension, we recommend using an option chain when using that extension:
|
|
947
|
-
|
|
948
|
-
```ts
|
|
949
|
-
import { pluginId as loggerPluginId } from 'your-auth-logger'
|
|
950
|
-
import type { PluginId as LoggerId, LoggerExtension } from 'your-logger-plugin'
|
|
951
|
-
|
|
952
|
-
export const pluginId = 'my:api' as const
|
|
953
|
-
export type PluginId = typeof pluginId
|
|
954
|
-
|
|
955
|
-
export interface ApiExtension {}
|
|
956
|
-
|
|
957
|
-
// Depend on optional plugin
|
|
958
|
-
const dependencies = [{ id: 'my:logger', optional: true }] as const
|
|
959
|
-
|
|
960
|
-
export default function api() {
|
|
961
|
-
return plugin<Record<LoggerId, LoggerExtension>, PluginId, typeof dependencies, ApiExtension>({
|
|
962
|
-
id: pluginId,
|
|
963
|
-
dependencies,
|
|
964
|
-
onExtension: ctx => {
|
|
965
|
-
// May be `undefined`
|
|
966
|
-
const logger = ctx.extensions[LoggerId]
|
|
967
|
-
|
|
968
|
-
// Safe access
|
|
969
|
-
logger?.log('message')
|
|
970
|
-
}
|
|
971
|
-
})
|
|
972
|
-
}
|
|
973
|
-
```
|
|
974
|
-
|
|
975
|
-
#### Command definition
|
|
976
|
-
|
|
977
|
-
If the `define` function has an implementation that depends on a specific plugin extension, it's recommended to use optional chaining to use the extension:
|
|
978
|
-
|
|
979
|
-
```ts
|
|
980
|
-
import { define } from 'gunshi'
|
|
981
|
-
import auth, { pluginId as authId } from 'your-auth-plugin'
|
|
982
|
-
import type { AuthExtension, PluginId as AuthId } from 'your-auth-plugin'
|
|
983
|
-
|
|
984
|
-
const myCommand = define<Record<AuthId, AuthExtension>>({
|
|
985
|
-
// ...
|
|
986
|
-
|
|
987
|
-
run: async ctx => {
|
|
988
|
-
const auth = ctx.extensions[authId] // Maybe `undefined`
|
|
989
|
-
|
|
990
|
-
// Use safelly extensions with optional chaining
|
|
991
|
-
if (auth?.isAuthenticated()) {
|
|
992
|
-
// For login ...
|
|
993
|
-
} else {
|
|
994
|
-
throw new Error('Please login first')
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
})
|
|
998
|
-
```
|
|
999
|
-
|
|
1000
|
-
## 📚 Official Plugins
|
|
1001
|
-
|
|
1002
|
-
Learn from gunshi's official plugins:
|
|
1003
|
-
|
|
1004
|
-
### `@gunshi/plugin-global`
|
|
1005
|
-
|
|
1006
|
-
- **Pattern**: Global options, command interception
|
|
1007
|
-
- **Learn**: How to add universal CLI features
|
|
1008
|
-
|
|
1009
|
-
### `@gunshi/plugin-i18n`
|
|
1010
|
-
|
|
1011
|
-
- **Pattern**: Complex extensions, resource loading
|
|
1012
|
-
- **Learn**: How to manage stateful extensions
|
|
1013
|
-
|
|
1014
|
-
### `@gunshi/plugin-renderer`
|
|
1015
|
-
|
|
1016
|
-
- **Pattern**: Renderer decorators
|
|
1017
|
-
- **Learn**: How to customize output formatting
|
|
1018
|
-
|
|
1019
|
-
### `@gunshi/plugin-completion`
|
|
1020
|
-
|
|
1021
|
-
- **Pattern**: Dynamic command generation
|
|
1022
|
-
- **Learn**: How to add utility commands
|
|
1023
|
-
|
|
1024
|
-
## 👥 Community Plugins
|
|
1025
|
-
|
|
1026
|
-
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
1027
|
-
|
|
1028
|
-
> [!NOTE]
|
|
1029
|
-
> Welcome your plugins! Submit a PR to add your plugin to this list.
|
|
1030
|
-
|
|
1031
|
-
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
1032
|
-
|
|
1033
|
-
## 📖 API Reference
|
|
1034
|
-
|
|
1035
|
-
TODO: referer the API section of gunshi docs
|
|
19
|
+
About details, See [the plugin development guides](https://gunshi.dev/guide/plugin/introduction) of official docs.
|
|
1036
20
|
|
|
1037
21
|
## ©️ License
|
|
1038
22
|
|
package/lib/index.d.ts
CHANGED
|
@@ -473,10 +473,10 @@ interface Args {
|
|
|
473
473
|
*
|
|
474
474
|
* @typeParam T - {@link Args | Arguments} which is an object that defines the command line arguments.
|
|
475
475
|
*/
|
|
476
|
-
type ArgValues<T> = T extends Args ? ResolveArgValues<T, { [Arg in keyof T]: ExtractOptionValue<T[Arg]> }> : {
|
|
476
|
+
type ArgValues<T$1> = T$1 extends Args ? ResolveArgValues<T$1, { [Arg in keyof T$1]: ExtractOptionValue<T$1[Arg]> }> : {
|
|
477
477
|
[option: string]: string | boolean | number | (string | boolean | number)[] | undefined;
|
|
478
478
|
};
|
|
479
|
-
type IsFunction<T> = T extends ((...args: any[]) => any) ? true : false;
|
|
479
|
+
type IsFunction<T$1> = T$1 extends ((...args: any[]) => any) ? true : false;
|
|
480
480
|
/**
|
|
481
481
|
* Extracts the value type from the argument schema.
|
|
482
482
|
*
|
|
@@ -484,8 +484,8 @@ type IsFunction<T> = T extends ((...args: any[]) => any) ? true : false;
|
|
|
484
484
|
*
|
|
485
485
|
* @internal
|
|
486
486
|
*/
|
|
487
|
-
type ExtractOptionValue<A extends ArgSchema> = A['type'] extends 'string' ? ResolveOptionValue<A, string> : A['type'] extends 'boolean' ? ResolveOptionValue<A, boolean> : A['type'] extends 'number' ? ResolveOptionValue<A, number> : A['type'] extends 'positional' ? ResolveOptionValue<A, string> : A['type'] extends 'enum' ? A['choices'] extends string[] | readonly string[] ? ResolveOptionValue<A, A['choices'][number]> : never : A['type'] extends 'custom' ? IsFunction<A['parse']> extends true ? ResolveOptionValue<A, ReturnType<NonNullable<A['parse']>>> : never : ResolveOptionValue<A, string | boolean | number>;
|
|
488
|
-
type ResolveOptionValue<A extends ArgSchema, T> = A['multiple'] extends true ? T[] : T;
|
|
487
|
+
type ExtractOptionValue<A$1 extends ArgSchema> = A$1['type'] extends 'string' ? ResolveOptionValue<A$1, string> : A$1['type'] extends 'boolean' ? ResolveOptionValue<A$1, boolean> : A$1['type'] extends 'number' ? ResolveOptionValue<A$1, number> : A$1['type'] extends 'positional' ? ResolveOptionValue<A$1, string> : A$1['type'] extends 'enum' ? A$1['choices'] extends string[] | readonly string[] ? ResolveOptionValue<A$1, A$1['choices'][number]> : never : A$1['type'] extends 'custom' ? IsFunction<A$1['parse']> extends true ? ResolveOptionValue<A$1, ReturnType<NonNullable<A$1['parse']>>> : never : ResolveOptionValue<A$1, string | boolean | number>;
|
|
488
|
+
type ResolveOptionValue<A$1 extends ArgSchema, T$1> = A$1['multiple'] extends true ? T$1[] : T$1;
|
|
489
489
|
/**
|
|
490
490
|
* Resolved argument values.
|
|
491
491
|
*
|
|
@@ -494,7 +494,7 @@ type ResolveOptionValue<A extends ArgSchema, T> = A['multiple'] extends true ? T
|
|
|
494
494
|
*
|
|
495
495
|
* @internal
|
|
496
496
|
*/
|
|
497
|
-
type ResolveArgValues<A extends Args, V extends Record<keyof A, unknown>> = { -readonly [Arg in keyof A]?: V[Arg] } & FilterArgs<A, V, 'default'> & FilterArgs<A, V, 'required'> & FilterPositionalArgs<A, V> extends infer P ? { [K in keyof P]: P[K] } : never;
|
|
497
|
+
type ResolveArgValues<A$1 extends Args, V extends Record<keyof A$1, unknown>> = { -readonly [Arg in keyof A$1]?: V[Arg] } & FilterArgs<A$1, V, 'default'> & FilterArgs<A$1, V, 'required'> & FilterPositionalArgs<A$1, V> extends infer P ? { [K in keyof P]: P[K] } : never;
|
|
498
498
|
/**
|
|
499
499
|
* Filters the arguments based on their default values.
|
|
500
500
|
*
|
|
@@ -504,7 +504,7 @@ type ResolveArgValues<A extends Args, V extends Record<keyof A, unknown>> = { -r
|
|
|
504
504
|
*
|
|
505
505
|
* @internal
|
|
506
506
|
*/
|
|
507
|
-
type FilterArgs<A extends Args, V extends Record<keyof A, unknown>, K extends keyof ArgSchema> = { [Arg in keyof A as A[Arg][K] extends {} ? Arg : never]: V[Arg] };
|
|
507
|
+
type FilterArgs<A$1 extends Args, V extends Record<keyof A$1, unknown>, K$1 extends keyof ArgSchema> = { [Arg in keyof A$1 as A$1[Arg][K$1] extends {} ? Arg : never]: V[Arg] };
|
|
508
508
|
/**
|
|
509
509
|
* Filters positional arguments from the argument schema.
|
|
510
510
|
*
|
|
@@ -513,7 +513,7 @@ type FilterArgs<A extends Args, V extends Record<keyof A, unknown>, K extends ke
|
|
|
513
513
|
*
|
|
514
514
|
* @internal
|
|
515
515
|
*/
|
|
516
|
-
type FilterPositionalArgs<A extends Args, V extends Record<keyof A, unknown>> = { [Arg in keyof A as A[Arg]['type'] extends 'positional' ? Arg : never]: V[Arg] };
|
|
516
|
+
type FilterPositionalArgs<A$1 extends Args, V extends Record<keyof A$1, unknown>> = { [Arg in keyof A$1 as A$1[Arg]['type'] extends 'positional' ? Arg : never]: V[Arg] };
|
|
517
517
|
/**
|
|
518
518
|
* An arguments for {@link resolveArgs | resolve arguments}.
|
|
519
519
|
*/
|
|
@@ -526,7 +526,7 @@ type FilterPositionalArgs<A extends Args, V extends Record<keyof A, unknown>> =
|
|
|
526
526
|
*
|
|
527
527
|
* @typeParam A - {@link Args | Arguments}, which is an object that defines the command line arguments.
|
|
528
528
|
*/
|
|
529
|
-
type ArgExplicitlyProvided<A extends Args> = { [K in keyof A]: boolean };
|
|
529
|
+
type ArgExplicitlyProvided<A$1 extends Args> = { [K in keyof A$1]: boolean };
|
|
530
530
|
/**
|
|
531
531
|
* Resolve command line arguments.
|
|
532
532
|
*
|
|
@@ -648,18 +648,18 @@ interface PluginContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
|
|
|
648
648
|
}
|
|
649
649
|
//#endregion
|
|
650
650
|
//#region ../gunshi/src/plugin/core.d.ts
|
|
651
|
-
type ProcessDependency<D, A extends ExtendContext> = D extends string ? D extends keyof A ? { [K in D]: A[K] } : {} : D extends {
|
|
651
|
+
type ProcessDependency<D, A$1 extends ExtendContext> = D extends string ? D extends keyof A$1 ? { [K in D]: A$1[K] } : {} : D extends {
|
|
652
652
|
id: infer ID;
|
|
653
653
|
optional?: any;
|
|
654
|
-
} ? ID extends string ? ID extends keyof A ? D extends {
|
|
654
|
+
} ? ID extends string ? ID extends keyof A$1 ? D extends {
|
|
655
655
|
optional: true;
|
|
656
|
-
} ? { [K in ID]: A[K] | undefined } : { [K in ID]: A[K] } : {} : never : never;
|
|
656
|
+
} ? { [K in ID]: A$1[K] | undefined } : { [K in ID]: A$1[K] } : {} : never : never;
|
|
657
657
|
/**
|
|
658
658
|
* Helper type to infer dependency extensions with optional support
|
|
659
659
|
*
|
|
660
660
|
* @internal
|
|
661
661
|
*/
|
|
662
|
-
type InferDependencyExtensions<D extends ReadonlyArray<PluginDependency | string>, A extends ExtendContext> = D extends readonly [] ? {} : D extends readonly [infer First, ...infer Rest] ? ProcessDependency<First, A> & (Rest extends ReadonlyArray<PluginDependency | string> ? InferDependencyExtensions<Rest, A> : {}) : {};
|
|
662
|
+
type InferDependencyExtensions<D extends ReadonlyArray<PluginDependency | string>, A$1 extends ExtendContext> = D extends readonly [] ? {} : D extends readonly [infer First, ...infer Rest] ? ProcessDependency<First, A$1> & (Rest extends ReadonlyArray<PluginDependency | string> ? InferDependencyExtensions<Rest, A$1> : {}) : {};
|
|
663
663
|
/**
|
|
664
664
|
* Plugin dependency definition
|
|
665
665
|
*
|
|
@@ -696,7 +696,7 @@ type PluginFunction<G extends GunshiParams = DefaultGunshiParams> = (ctx: Readon
|
|
|
696
696
|
*
|
|
697
697
|
* @since v0.27.0
|
|
698
698
|
*/
|
|
699
|
-
type PluginExtension<T = Record<string, unknown>, G extends GunshiParams = DefaultGunshiParams> = (ctx: CommandContextCore<G>, cmd: Command<G>) => Awaitable<T>;
|
|
699
|
+
type PluginExtension<T$1 = Record<string, unknown>, G extends GunshiParams = DefaultGunshiParams> = (ctx: CommandContextCore<G>, cmd: Command<G>) => Awaitable<T$1>;
|
|
700
700
|
/**
|
|
701
701
|
* Plugin extension callback, which is called when the plugin is extended with `extension` option.
|
|
702
702
|
*
|
|
@@ -765,11 +765,11 @@ ResolvedDepExt extends GunshiParams = GunshiParams<{
|
|
|
765
765
|
*
|
|
766
766
|
* @since v0.27.0
|
|
767
767
|
*/
|
|
768
|
-
type Plugin<E extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> = PluginFunction & {
|
|
768
|
+
type Plugin<E$1 extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> = PluginFunction & {
|
|
769
769
|
id: string;
|
|
770
770
|
name?: string;
|
|
771
771
|
dependencies?: (PluginDependency | string)[];
|
|
772
|
-
extension?: CommandContextExtension<E>;
|
|
772
|
+
extension?: CommandContextExtension<E$1>;
|
|
773
773
|
};
|
|
774
774
|
/**
|
|
775
775
|
* Plugin return type with extension, which includes the plugin ID, name, dependencies, and extension.
|
|
@@ -778,7 +778,7 @@ type Plugin<E extends GunshiParams['extensions'] = DefaultGunshiParams['extensio
|
|
|
778
778
|
*
|
|
779
779
|
* @typeParam E - A type extending {@link GunshiParams} to specify the shape of {@linkcode CommandContext}'s extensions.
|
|
780
780
|
*/
|
|
781
|
-
interface PluginWithExtension<E extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> extends Plugin<E> {
|
|
781
|
+
interface PluginWithExtension<E$1 extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> extends Plugin<E$1> {
|
|
782
782
|
/**
|
|
783
783
|
* Plugin identifier
|
|
784
784
|
*/
|
|
@@ -794,7 +794,7 @@ interface PluginWithExtension<E extends GunshiParams['extensions'] = DefaultGuns
|
|
|
794
794
|
/**
|
|
795
795
|
* Plugin extension
|
|
796
796
|
*/
|
|
797
|
-
extension: CommandContextExtension<E>;
|
|
797
|
+
extension: CommandContextExtension<E$1>;
|
|
798
798
|
}
|
|
799
799
|
/**
|
|
800
800
|
* Plugin return type without extension, which includes the plugin ID, name, and dependencies, but no extension.
|
|
@@ -803,7 +803,7 @@ interface PluginWithExtension<E extends GunshiParams['extensions'] = DefaultGuns
|
|
|
803
803
|
*
|
|
804
804
|
* @typeParam E - A type extending {@link GunshiParams} to specify the shape of {@linkcode CommandContext}'s extensions.
|
|
805
805
|
*/
|
|
806
|
-
interface PluginWithoutExtension<E extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> extends Plugin<E> {
|
|
806
|
+
interface PluginWithoutExtension<E$1 extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> extends Plugin<E$1> {
|
|
807
807
|
/**
|
|
808
808
|
* Plugin identifier
|
|
809
809
|
*/
|
|
@@ -899,13 +899,13 @@ ResolvedDepExtensions extends GunshiParams = GunshiParams<{
|
|
|
899
899
|
*
|
|
900
900
|
* @typeParam T - The type of the value that can be awaited.
|
|
901
901
|
*/
|
|
902
|
-
type Awaitable<T> = T | Promise<T>;
|
|
902
|
+
type Awaitable<T$1> = T$1 | Promise<T$1>;
|
|
903
903
|
/**
|
|
904
904
|
* Prettify a type by flattening its structure.
|
|
905
905
|
*
|
|
906
906
|
* @typeParam T - The type to be prettified.
|
|
907
907
|
*/
|
|
908
|
-
type Prettify<T> = { [K in keyof T]: T[K] } & {};
|
|
908
|
+
type Prettify<T$1> = { [K in keyof T$1]: T$1[K] } & {};
|
|
909
909
|
/**
|
|
910
910
|
* Extend command context type. This type is used to extend the command context with additional properties at {@linkcode CommandContext.extensions}.
|
|
911
911
|
*
|
|
@@ -921,7 +921,7 @@ type ExtendContext = Record<string, unknown>;
|
|
|
921
921
|
*
|
|
922
922
|
* @since v0.27.0
|
|
923
923
|
*/
|
|
924
|
-
interface GunshiParams<P extends {
|
|
924
|
+
interface GunshiParams<P$1 extends {
|
|
925
925
|
args?: Args;
|
|
926
926
|
extensions?: ExtendContext;
|
|
927
927
|
} = {
|
|
@@ -931,13 +931,13 @@ interface GunshiParams<P extends {
|
|
|
931
931
|
/**
|
|
932
932
|
* Command argument definitions.
|
|
933
933
|
*/
|
|
934
|
-
args: P extends {
|
|
934
|
+
args: P$1 extends {
|
|
935
935
|
args: infer A extends Args;
|
|
936
936
|
} ? A : Args;
|
|
937
937
|
/**
|
|
938
938
|
* Command context extensions.
|
|
939
939
|
*/
|
|
940
|
-
extensions: P extends {
|
|
940
|
+
extensions: P$1 extends {
|
|
941
941
|
extensions: infer E extends ExtendContext;
|
|
942
942
|
} ? E : {};
|
|
943
943
|
}
|
|
@@ -1314,7 +1314,7 @@ type CommandContextCore<G extends GunshiParamsConstraint = DefaultGunshiParams>
|
|
|
1314
1314
|
*
|
|
1315
1315
|
* @since v0.27.0
|
|
1316
1316
|
*/
|
|
1317
|
-
interface CommandContextExtension<E extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> {
|
|
1317
|
+
interface CommandContextExtension<E$1 extends GunshiParams['extensions'] = DefaultGunshiParams['extensions']> {
|
|
1318
1318
|
/**
|
|
1319
1319
|
* Plugin identifier
|
|
1320
1320
|
*/
|
|
@@ -1322,7 +1322,7 @@ interface CommandContextExtension<E extends GunshiParams['extensions'] = Default
|
|
|
1322
1322
|
/**
|
|
1323
1323
|
* Plugin extension factory
|
|
1324
1324
|
*/
|
|
1325
|
-
readonly factory: (ctx: CommandContextCore, cmd: Command) => Awaitable<E>;
|
|
1325
|
+
readonly factory: (ctx: CommandContextCore, cmd: Command) => Awaitable<E$1>;
|
|
1326
1326
|
/**
|
|
1327
1327
|
* Plugin extension factory after hook
|
|
1328
1328
|
*/
|
|
@@ -1480,7 +1480,7 @@ type CommandDecorator<G extends GunshiParamsConstraint = DefaultGunshiParams> =
|
|
|
1480
1480
|
*
|
|
1481
1481
|
* @since v0.27.0
|
|
1482
1482
|
*/
|
|
1483
|
-
type RendererDecorator<T, G extends GunshiParamsConstraint = DefaultGunshiParams> = (baseRenderer: (ctx: Readonly<CommandContext<G>>) => Promise<T>, ctx: Readonly<CommandContext<G>>) => Promise<T>;
|
|
1483
|
+
type RendererDecorator<T$1, G extends GunshiParamsConstraint = DefaultGunshiParams> = (baseRenderer: (ctx: Readonly<CommandContext<G>>) => Promise<T$1>, ctx: Readonly<CommandContext<G>>) => Promise<T$1>;
|
|
1484
1484
|
/**
|
|
1485
1485
|
* Validation errors renderer decorator type.
|
|
1486
1486
|
* A function that wraps a validation errors renderer to add or modify its behavior.
|
|
@@ -1506,7 +1506,7 @@ declare const CLI_OPTIONS_DEFAULT: CliOptions<DefaultGunshiParams>;
|
|
|
1506
1506
|
*
|
|
1507
1507
|
* @internal
|
|
1508
1508
|
*/
|
|
1509
|
-
type ExtractExtensions$1<E extends Record<string, CommandContextExtension>> = { [K in keyof E]: E[K] extends CommandContextExtension<infer T> ? T : never };
|
|
1509
|
+
type ExtractExtensions$1<E$1 extends Record<string, CommandContextExtension>> = { [K in keyof E$1]: E$1[K] extends CommandContextExtension<infer T> ? T : never };
|
|
1510
1510
|
/**
|
|
1511
1511
|
* Parameters of {@link createCommandContext}
|
|
1512
1512
|
*/
|
|
@@ -1514,7 +1514,7 @@ interface CommandContextParams<G extends GunshiParams | {
|
|
|
1514
1514
|
args: Args;
|
|
1515
1515
|
} | {
|
|
1516
1516
|
extensions: ExtendContext;
|
|
1517
|
-
}, V extends ArgValues<ExtractArgs<G>>, C extends Command<G> | LazyCommand<G> = Command<G>, E extends Record<string, CommandContextExtension> = Record<string, CommandContextExtension>> {
|
|
1517
|
+
}, V extends ArgValues<ExtractArgs<G>>, C extends Command<G> | LazyCommand<G> = Command<G>, E$1 extends Record<string, CommandContextExtension> = Record<string, CommandContextExtension>> {
|
|
1518
1518
|
/**
|
|
1519
1519
|
* An arguments of target command
|
|
1520
1520
|
*/
|
|
@@ -1558,7 +1558,7 @@ interface CommandContextParams<G extends GunshiParams | {
|
|
|
1558
1558
|
/**
|
|
1559
1559
|
* Plugin extensions to apply as the command context extension.
|
|
1560
1560
|
*/
|
|
1561
|
-
extensions?: E;
|
|
1561
|
+
extensions?: E$1;
|
|
1562
1562
|
/**
|
|
1563
1563
|
* A command options, which is spicialized from `cli` function
|
|
1564
1564
|
*/
|
|
@@ -1574,7 +1574,7 @@ interface CommandContextParams<G extends GunshiParams | {
|
|
|
1574
1574
|
* @param param - A {@link CommandContextParams | parameters} to create a command context.
|
|
1575
1575
|
* @returns A {@link CommandContext | command context}, which is readonly.
|
|
1576
1576
|
*/
|
|
1577
|
-
declare function createCommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams, V extends ArgValues<ExtractArgs<G>> = ArgValues<ExtractArgs<G>>, C extends Command<G> | LazyCommand<G> = Command<G>, E extends Record<string, CommandContextExtension> = {}>({
|
|
1577
|
+
declare function createCommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams, V extends ArgValues<ExtractArgs<G>> = ArgValues<ExtractArgs<G>>, C extends Command<G> | LazyCommand<G> = Command<G>, E$1 extends Record<string, CommandContextExtension> = {}>({
|
|
1578
1578
|
args,
|
|
1579
1579
|
explicit,
|
|
1580
1580
|
values,
|
|
@@ -1588,9 +1588,9 @@ declare function createCommandContext<G extends GunshiParamsConstraint = Default
|
|
|
1588
1588
|
callMode,
|
|
1589
1589
|
omitted,
|
|
1590
1590
|
validationError
|
|
1591
|
-
}: CommandContextParams<G, V, C, E>): Promise<{} extends ExtractExtensions$1<E> ? Readonly<CommandContext<G>> : Readonly<CommandContext<GunshiParams<{
|
|
1591
|
+
}: CommandContextParams<G, V, C, E$1>): Promise<{} extends ExtractExtensions$1<E$1> ? Readonly<CommandContext<G>> : Readonly<CommandContext<GunshiParams<{
|
|
1592
1592
|
args: ExtractArgs<G>;
|
|
1593
|
-
extensions: ExtractExtensions$1<E>;
|
|
1593
|
+
extensions: ExtractExtensions$1<E$1>;
|
|
1594
1594
|
}>>>>;
|
|
1595
1595
|
//#endregion
|
|
1596
1596
|
export { ANONYMOUS_COMMAND_NAME, type ArgSchema, type ArgToken, type ArgValues, type Args, Awaitable, CLI_OPTIONS_DEFAULT, Command, CommandContext, CommandContextCore, CommandContextExtension, CommandContextParams, CommandDecorator, CommandExamplesFetcher, CommandRunner, DefaultGunshiParams, ExtendContext, ExtractArgs, ExtractExtensions, GunshiParams, GunshiParamsConstraint, LazyCommand, NormalizeToGunshiParams, OnPluginExtension, Plugin, PluginContext, PluginDependency, PluginExtension, PluginFunction, PluginOptions, PluginWithExtension, PluginWithoutExtension, Prettify, RendererDecorator, ValidationErrorsDecorator, createCommandContext, plugin };
|
package/lib/index.js
CHANGED
|
@@ -70,6 +70,21 @@ function deepFreeze(obj, ignores = []) {
|
|
|
70
70
|
//#endregion
|
|
71
71
|
//#region ../gunshi/src/context.ts
|
|
72
72
|
/**
|
|
73
|
+
* The entry for gunshi context.
|
|
74
|
+
* This module is exported for the purpose of testing the command.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```js
|
|
78
|
+
* import { createCommandContext } from 'gunshi/context'
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @module
|
|
82
|
+
*/
|
|
83
|
+
/**
|
|
84
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
85
|
+
* @license MIT
|
|
86
|
+
*/
|
|
87
|
+
/**
|
|
73
88
|
* Create a command context.
|
|
74
89
|
*
|
|
75
90
|
* @param param - A {@link CommandContextParams | parameters} to create a command context.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gunshi/plugin",
|
|
3
3
|
"description": "plugin development kit for gunshi",
|
|
4
|
-
"version": "0.27.0-beta.
|
|
4
|
+
"version": "0.27.0-beta.4",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "kazuya kawaguchi",
|
|
7
7
|
"email": "kawakazu80@gmail.com"
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"deno": "^2.5.
|
|
54
|
+
"deno": "^2.5.6",
|
|
55
55
|
"jsr": "^0.13.5",
|
|
56
56
|
"jsr-exports-lint": "^0.4.1",
|
|
57
|
-
"publint": "^0.3.
|
|
58
|
-
"tsdown": "
|
|
59
|
-
"gunshi": "0.27.0-beta.
|
|
57
|
+
"publint": "^0.3.15",
|
|
58
|
+
"tsdown": "0.15.12",
|
|
59
|
+
"gunshi": "0.27.0-beta.4"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsdown",
|