gunshi 0.5.2 → 0.5.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 +70 -64
- package/lib/{context-B5z7lnoV.js → context-DmZAeiph.js} +2 -23
- package/lib/context.d.ts +2 -2
- package/lib/context.js +2 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +3 -190
- package/lib/renderer/index.d.ts +26 -0
- package/lib/renderer/index.js +4 -0
- package/lib/renderer-MpQ9U28q.js +192 -0
- package/lib/{types.d-veidyA82.d.ts → types.d-CxaX4FVV.d.ts} +15 -11
- package/lib/utils-NHs5DuHk.js +24 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -18,8 +18,8 @@ Gunshi is a modern javascript command-line library
|
|
|
18
18
|
Gunshi is designed to simplify the creation of modern command-line interfaces:
|
|
19
19
|
|
|
20
20
|
- 📏 **Simple**: Run the commands with a simple API.
|
|
21
|
-
- 🛡️ **Type Safe**: Arguments parsing and options value resolution type-safely by [args-tokens](https://github.com/kazupon/args-tokens)
|
|
22
21
|
- ⚙️ **Declarative configuration**: Configure the command modules declaratively.
|
|
22
|
+
- 🛡️ **Type Safe**: Arguments parsing and options value resolution type-safely by [args-tokens](https://github.com/kazupon/args-tokens)
|
|
23
23
|
- 🧩 **Composable**: Sub-commands that can be composed with modularized commands.
|
|
24
24
|
- ⏳ **Lazy & Async**: Command modules lazy loading and asynchronously executing.
|
|
25
25
|
- 📜 **Auto usage generation**: Automatic usage message generation with modularized commands.
|
|
@@ -62,52 +62,12 @@ Gunshi has a simple API that is a facade:
|
|
|
62
62
|
```js
|
|
63
63
|
import { cli } from 'gunshi'
|
|
64
64
|
|
|
65
|
-
//
|
|
65
|
+
// run a simple command
|
|
66
66
|
cli(process.argv.slice(2), () => {
|
|
67
67
|
console.log('Hello from Gunshi!')
|
|
68
68
|
})
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
### 🛡️ Type-Safe Arguments
|
|
72
|
-
|
|
73
|
-
Gunshi provides type-safe argument parsing with TypeScript:
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
import { cli } from 'gunshi'
|
|
77
|
-
import type { ArgOptions, Command, CommandContext } from 'gunshi'
|
|
78
|
-
|
|
79
|
-
// Define interfaces for options and values
|
|
80
|
-
interface UserOptions extends ArgOptions {
|
|
81
|
-
name: { type: 'string'; short: 'n' }
|
|
82
|
-
age: { type: 'number'; short: 'a'; default: number }
|
|
83
|
-
verbose: { type: 'boolean'; short: 'v' }
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
interface UserValues {
|
|
87
|
-
name?: string
|
|
88
|
-
age: number
|
|
89
|
-
verbose?: boolean
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Create a type-safe command
|
|
93
|
-
const command: Command<UserOptions> = {
|
|
94
|
-
name: 'type-safe',
|
|
95
|
-
options: {
|
|
96
|
-
name: { type: 'string', short: 'n' },
|
|
97
|
-
age: { type: 'number', short: 'a', default: 25 },
|
|
98
|
-
verbose: { type: 'boolean', short: 'v' }
|
|
99
|
-
},
|
|
100
|
-
run: (ctx: CommandContext<UserOptions, UserValues>) => {
|
|
101
|
-
const { name, age, verbose } = ctx.values
|
|
102
|
-
console.log(`Hello, ${name || 'World'}! You are ${age} years old.`)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
await cli(process.argv.slice(2), command)
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
For more detailed examples, check out the [playground/type-safe](https://github.com/kazupon/gunshi/tree/main/playground/type-safe) in the repository.
|
|
110
|
-
|
|
111
71
|
### ⚙️ Declarative Configuration
|
|
112
72
|
|
|
113
73
|
Configure commands declaratively:
|
|
@@ -115,7 +75,7 @@ Configure commands declaratively:
|
|
|
115
75
|
```js
|
|
116
76
|
import { cli } from 'gunshi'
|
|
117
77
|
|
|
118
|
-
//
|
|
78
|
+
// define a command with declarative configuration
|
|
119
79
|
const command = {
|
|
120
80
|
name: 'greet',
|
|
121
81
|
description: 'A greeting command',
|
|
@@ -148,6 +108,52 @@ cli(process.argv.slice(2), command, {
|
|
|
148
108
|
|
|
149
109
|
For more detailed examples, check out the [playground/declarative](https://github.com/kazupon/gunshi/tree/main/playground/declarative) in the repository.
|
|
150
110
|
|
|
111
|
+
### 🛡️ Type-Safe Arguments
|
|
112
|
+
|
|
113
|
+
Gunshi provides type-safe argument parsing with TypeScript:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { cli } from 'gunshi'
|
|
117
|
+
import type { ArgOptions, Command, CommandContext } from 'gunshi'
|
|
118
|
+
|
|
119
|
+
// type-safe arguments parsing example
|
|
120
|
+
// this demonstrates how to define and use typed command options with `satisfies`
|
|
121
|
+
|
|
122
|
+
// define options with types
|
|
123
|
+
const options = {
|
|
124
|
+
// define string option with short alias
|
|
125
|
+
name: {
|
|
126
|
+
type: 'string',
|
|
127
|
+
short: 'n'
|
|
128
|
+
},
|
|
129
|
+
// define number option with default value
|
|
130
|
+
age: {
|
|
131
|
+
type: 'number',
|
|
132
|
+
short: 'a',
|
|
133
|
+
default: 25
|
|
134
|
+
},
|
|
135
|
+
// define boolean flag
|
|
136
|
+
verbose: {
|
|
137
|
+
type: 'boolean',
|
|
138
|
+
short: 'v'
|
|
139
|
+
}
|
|
140
|
+
} satisfies ArgOptions
|
|
141
|
+
|
|
142
|
+
// create a type-safe command
|
|
143
|
+
const command = {
|
|
144
|
+
name: 'type-safe',
|
|
145
|
+
options,
|
|
146
|
+
run: (ctx: CommandContext<UserOptions, UserValues>) => {
|
|
147
|
+
const { name, age, verbose } = ctx.values
|
|
148
|
+
console.log(`Hello, ${name || 'World'}! You are ${age} years old.`)
|
|
149
|
+
}
|
|
150
|
+
} satisfies Command<typeof options>
|
|
151
|
+
|
|
152
|
+
await cli(process.argv.slice(2), command)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
For more detailed examples, check out the [playground/type-safe](https://github.com/kazupon/gunshi/tree/main/playground/type-safe) in the repository.
|
|
156
|
+
|
|
151
157
|
### 🧩 Composable Sub-commands
|
|
152
158
|
|
|
153
159
|
Create a CLI with composable sub-commands:
|
|
@@ -155,7 +161,7 @@ Create a CLI with composable sub-commands:
|
|
|
155
161
|
```js
|
|
156
162
|
import { cli } from 'gunshi'
|
|
157
163
|
|
|
158
|
-
//
|
|
164
|
+
// define sub-commands
|
|
159
165
|
const createCommand = {
|
|
160
166
|
name: 'create',
|
|
161
167
|
description: 'Create a new resource',
|
|
@@ -175,12 +181,12 @@ const listCommand = {
|
|
|
175
181
|
}
|
|
176
182
|
}
|
|
177
183
|
|
|
178
|
-
//
|
|
184
|
+
// create a Map of sub-commands
|
|
179
185
|
const subCommands = new Map()
|
|
180
186
|
subCommands.set('create', createCommand)
|
|
181
187
|
subCommands.set('list', listCommand)
|
|
182
188
|
|
|
183
|
-
//
|
|
189
|
+
// define the main command
|
|
184
190
|
const mainCommand = {
|
|
185
191
|
name: 'resource-manager',
|
|
186
192
|
description: 'Manage resources',
|
|
@@ -189,7 +195,7 @@ const mainCommand = {
|
|
|
189
195
|
}
|
|
190
196
|
}
|
|
191
197
|
|
|
192
|
-
//
|
|
198
|
+
// run the CLI with composable sub-commands
|
|
193
199
|
cli(process.argv.slice(2), mainCommand, {
|
|
194
200
|
name: 'my-app',
|
|
195
201
|
version: '1.0.0',
|
|
@@ -206,28 +212,28 @@ Load commands lazily and execute them asynchronously:
|
|
|
206
212
|
```js
|
|
207
213
|
import { cli } from 'gunshi'
|
|
208
214
|
|
|
209
|
-
//
|
|
215
|
+
// define a command that will be loaded lazily
|
|
210
216
|
const lazyCommand = async () => {
|
|
211
|
-
//
|
|
217
|
+
// simulate async loading
|
|
212
218
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
213
219
|
|
|
214
|
-
//
|
|
220
|
+
// return the actual command
|
|
215
221
|
return {
|
|
216
222
|
name: 'lazy',
|
|
217
223
|
description: 'A command that is loaded lazily',
|
|
218
224
|
run: async ctx => {
|
|
219
|
-
//
|
|
225
|
+
// async execution
|
|
220
226
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
221
227
|
console.log('Command executed!')
|
|
222
228
|
}
|
|
223
229
|
}
|
|
224
230
|
}
|
|
225
231
|
|
|
226
|
-
//
|
|
232
|
+
// create a Map of sub-commands with lazy-loaded commands
|
|
227
233
|
const subCommands = new Map()
|
|
228
234
|
subCommands.set('lazy', lazyCommand)
|
|
229
235
|
|
|
230
|
-
//
|
|
236
|
+
// run the CLI with lazy-loaded commands
|
|
231
237
|
cli(
|
|
232
238
|
process.argv.slice(2),
|
|
233
239
|
{ name: 'main', run: () => {} },
|
|
@@ -264,11 +270,11 @@ const command = {
|
|
|
264
270
|
examples: '# Example\n$ my-app --operation list --path ./src'
|
|
265
271
|
},
|
|
266
272
|
run: ctx => {
|
|
267
|
-
//
|
|
273
|
+
// command implementation
|
|
268
274
|
}
|
|
269
275
|
}
|
|
270
276
|
|
|
271
|
-
//
|
|
277
|
+
// run with --help to see the automatically generated usage information
|
|
272
278
|
cli(process.argv.slice(2), command, {
|
|
273
279
|
name: 'my-app',
|
|
274
280
|
version: '1.0.0'
|
|
@@ -284,7 +290,7 @@ Customize the usage message generation:
|
|
|
284
290
|
```js
|
|
285
291
|
import { cli } from 'gunshi'
|
|
286
292
|
|
|
287
|
-
//
|
|
293
|
+
// custom header renderer
|
|
288
294
|
const customHeaderRenderer = ctx => {
|
|
289
295
|
return Promise.resolve(`
|
|
290
296
|
╔═══════════════════════╗
|
|
@@ -295,7 +301,7 @@ Version: ${ctx.env.version}
|
|
|
295
301
|
`)
|
|
296
302
|
}
|
|
297
303
|
|
|
298
|
-
//
|
|
304
|
+
// custom usage renderer
|
|
299
305
|
const customUsageRenderer = ctx => {
|
|
300
306
|
const lines = []
|
|
301
307
|
lines.push('USAGE:')
|
|
@@ -303,7 +309,7 @@ const customUsageRenderer = ctx => {
|
|
|
303
309
|
lines.push('')
|
|
304
310
|
lines.push('OPTIONS:')
|
|
305
311
|
|
|
306
|
-
for (const [key, option] of Object.entries(ctx.options ||
|
|
312
|
+
for (const [key, option] of Object.entries(ctx.options || Object.create(null))) {
|
|
307
313
|
const shortFlag = option.short ? `-${option.short}, ` : ' '
|
|
308
314
|
lines.push(` ${shortFlag}--${key.padEnd(10)} ${ctx.translation(key)}`)
|
|
309
315
|
}
|
|
@@ -311,7 +317,7 @@ const customUsageRenderer = ctx => {
|
|
|
311
317
|
return Promise.resolve(lines.join('\n'))
|
|
312
318
|
}
|
|
313
319
|
|
|
314
|
-
//
|
|
320
|
+
// run with custom renderers
|
|
315
321
|
cli(
|
|
316
322
|
process.argv.slice(2),
|
|
317
323
|
{ name: 'app', run: () => {} },
|
|
@@ -341,14 +347,14 @@ const command = {
|
|
|
341
347
|
name: { type: 'string', short: 'n' },
|
|
342
348
|
formal: { type: 'boolean', short: 'f' }
|
|
343
349
|
},
|
|
344
|
-
//
|
|
350
|
+
// resource fetcher for translations
|
|
345
351
|
resource: async ctx => {
|
|
346
352
|
if (ctx.locale.toString() === 'ja-JP') {
|
|
347
353
|
const resource = await import('./locales/ja-JP.json', { with: { type: 'json' } })
|
|
348
354
|
return resource.default
|
|
349
355
|
}
|
|
350
356
|
|
|
351
|
-
//
|
|
357
|
+
// default to English
|
|
352
358
|
return enUS
|
|
353
359
|
},
|
|
354
360
|
run: ctx => {
|
|
@@ -358,12 +364,12 @@ const command = {
|
|
|
358
364
|
}
|
|
359
365
|
}
|
|
360
366
|
|
|
361
|
-
//
|
|
367
|
+
// run with locale support
|
|
362
368
|
cli(process.argv.slice(2), command, {
|
|
363
369
|
name: 'my-app',
|
|
364
370
|
version: '1.0.0',
|
|
365
|
-
//
|
|
366
|
-
//
|
|
371
|
+
// set the locale via an environment variable
|
|
372
|
+
// if Node v21 or later is used, you can use the built-in `navigator.language` instead)
|
|
367
373
|
locale: new Intl.Locale(process.env.MY_LOCALE || 'en-US')
|
|
368
374
|
})
|
|
369
375
|
```
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { create, deepFreeze, resolveLazyCommand } from "./utils-NHs5DuHk.js";
|
|
1
2
|
|
|
2
3
|
//#region locales/en-US.json
|
|
3
4
|
var COMMAND = "COMMAND";
|
|
@@ -56,28 +57,6 @@ const COMMAND_I18N_RESOURCE_KEYS = [
|
|
|
56
57
|
"FORMORE"
|
|
57
58
|
];
|
|
58
59
|
|
|
59
|
-
//#endregion
|
|
60
|
-
//#region src/utils.ts
|
|
61
|
-
async function resolveLazyCommand(cmd, name, entry = false) {
|
|
62
|
-
const resolved = Object.assign(create(), typeof cmd == "function" ? await cmd() : cmd, { default: entry });
|
|
63
|
-
if (resolved.name == null && name) resolved.name = name;
|
|
64
|
-
return deepFreeze(resolved);
|
|
65
|
-
}
|
|
66
|
-
function create(obj = null) {
|
|
67
|
-
return Object.create(obj);
|
|
68
|
-
}
|
|
69
|
-
function log(...args) {
|
|
70
|
-
console.log(...args);
|
|
71
|
-
}
|
|
72
|
-
function deepFreeze(obj) {
|
|
73
|
-
if (obj === null || typeof obj !== "object") return obj;
|
|
74
|
-
for (const key of Object.keys(obj)) {
|
|
75
|
-
const value = obj[key];
|
|
76
|
-
if (typeof value === "object" && value !== null) deepFreeze(value);
|
|
77
|
-
}
|
|
78
|
-
return Object.freeze(obj);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
60
|
//#endregion
|
|
82
61
|
//#region src/context.ts
|
|
83
62
|
const DEFAULT_LOCALE = "en-US";
|
|
@@ -196,4 +175,4 @@ async function loadCommandResource(ctx, command) {
|
|
|
196
175
|
}
|
|
197
176
|
|
|
198
177
|
//#endregion
|
|
199
|
-
export { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, DEFAULT_LOCALE,
|
|
178
|
+
export { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, DEFAULT_LOCALE, createCommandContext };
|
package/lib/context.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ArgOptions, ArgValues } from 'args-tokens';
|
|
2
|
-
import { C as Command, a as CommandOptions, b as CommandContext } from './types.d-
|
|
2
|
+
import { C as Command, a as CommandOptions, b as CommandContext } from './types.d-CxaX4FVV.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The default locale string, which format is BCP 47 language tag
|
|
@@ -43,7 +43,7 @@ interface CommandContextParams<
|
|
|
43
43
|
* @returns A {@link CommandContext | command context}, which is readonly
|
|
44
44
|
*/
|
|
45
45
|
declare function createCommandContext<
|
|
46
|
-
Options extends ArgOptions,
|
|
46
|
+
Options extends ArgOptions = ArgOptions,
|
|
47
47
|
Values = ArgValues<Options>
|
|
48
48
|
>({ options, values, positionals, command, commandOptions, omitted }: CommandContextParams<Options, Values>): Promise<Readonly<CommandContext<Options, Values>>>;
|
|
49
49
|
|
package/lib/context.js
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ArgOptions } from 'args-tokens';
|
|
2
2
|
export { ArgOptionSchema, ArgOptions, ArgValues } from 'args-tokens';
|
|
3
|
-
import { C as Command, c as CommandRunner, a as CommandOptions } from './types.d-
|
|
4
|
-
export { f as CommandBuiltinKeys, d as CommandBuiltinOptionsKeys, e as CommandBuiltinResourceKeys, b as CommandContext, g as CommandEnvironment, h as CommandResource, i as CommandResourceFetcher, L as LazyCommand } from './types.d-
|
|
3
|
+
import { C as Command, c as CommandRunner, a as CommandOptions } from './types.d-CxaX4FVV.js';
|
|
4
|
+
export { f as CommandBuiltinKeys, d as CommandBuiltinOptionsKeys, e as CommandBuiltinResourceKeys, b as CommandContext, g as CommandEnvironment, h as CommandResource, i as CommandResourceFetcher, j as Commandable, L as LazyCommand } from './types.d-CxaX4FVV.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Run the command
|
|
@@ -9,6 +9,6 @@ export { f as CommandBuiltinKeys, d as CommandBuiltinOptionsKeys, e as CommandBu
|
|
|
9
9
|
* @param entry - A {@link Command | entry command} or an {@link CommandRunner | inline command runner}
|
|
10
10
|
* @param opts - A {@link CommandOptions | command options}
|
|
11
11
|
*/
|
|
12
|
-
declare function cli<Options extends ArgOptions>(args: string[], entry: Command<Options> | CommandRunner<Options>, opts?: CommandOptions<Options>): Promise<void>;
|
|
12
|
+
declare function cli<Options extends ArgOptions = ArgOptions>(args: string[], entry: Command<Options> | CommandRunner<Options>, opts?: CommandOptions<Options>): Promise<void>;
|
|
13
13
|
|
|
14
14
|
export { Command, CommandOptions, CommandRunner, cli };
|
package/lib/index.js
CHANGED
|
@@ -1,195 +1,8 @@
|
|
|
1
|
-
import { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS,
|
|
1
|
+
import { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, createCommandContext } from "./context-DmZAeiph.js";
|
|
2
|
+
import { create, log, resolveLazyCommand } from "./utils-NHs5DuHk.js";
|
|
3
|
+
import { renderHeader, renderUsage, renderValidationErrors } from "./renderer-MpQ9U28q.js";
|
|
2
4
|
import { parseArgs, resolveArgs } from "args-tokens";
|
|
3
5
|
|
|
4
|
-
//#region src/renderer/header.ts
|
|
5
|
-
function renderHeader(ctx) {
|
|
6
|
-
const title = ctx.env.description || ctx.env.name || "";
|
|
7
|
-
return Promise.resolve(title ? `${title} (${ctx.env.name || ""}${ctx.env.version ? ` v${ctx.env.version}` : ""})` : title);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
//#endregion
|
|
11
|
-
//#region src/renderer/usage.ts
|
|
12
|
-
async function renderUsage(ctx) {
|
|
13
|
-
const messages = [];
|
|
14
|
-
if (!ctx.omitted && hasDescription(ctx)) messages.push(ctx.description, "");
|
|
15
|
-
messages.push(...await renderUsageSection(ctx), "");
|
|
16
|
-
if (ctx.omitted && await hasCommands(ctx)) messages.push(...await renderCommandsSection(ctx), "");
|
|
17
|
-
if (hasOptions(ctx)) messages.push(...await renderOptionsSection(ctx), "");
|
|
18
|
-
if (hasExamples(ctx)) messages.push(...renderExamplesSection(ctx), "");
|
|
19
|
-
return messages.join("\n");
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Render the options section
|
|
23
|
-
* @param ctx A {@link CommandContext | command context}
|
|
24
|
-
* @returns A rendered options section
|
|
25
|
-
*/
|
|
26
|
-
async function renderOptionsSection(ctx) {
|
|
27
|
-
const messages = [];
|
|
28
|
-
messages.push(`${ctx.translation("OPTIONS")}:`);
|
|
29
|
-
const optionsPairs = getOptionsPairs(ctx);
|
|
30
|
-
messages.push(await generateOptionsUsage(ctx, optionsPairs));
|
|
31
|
-
return messages;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Render the examples section
|
|
35
|
-
* @param ctx A {@link CommandContext | command context}
|
|
36
|
-
* @returns A rendered examples section
|
|
37
|
-
*/
|
|
38
|
-
function renderExamplesSection(ctx) {
|
|
39
|
-
const messages = [];
|
|
40
|
-
const examples = ctx.usage.examples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
|
|
41
|
-
messages.push(`${ctx.translation("EXAMPLES")}:`, ...examples);
|
|
42
|
-
return messages;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Render the usage section
|
|
46
|
-
* @param ctx A {@link CommandContext | command context}
|
|
47
|
-
* @returns A rendered usage section
|
|
48
|
-
*/
|
|
49
|
-
async function renderUsageSection(ctx) {
|
|
50
|
-
const messages = [`${ctx.translation("USAGE")}:`];
|
|
51
|
-
if (ctx.omitted) {
|
|
52
|
-
const defaultCommand = `${resolveEntry(ctx)}${await hasCommands(ctx) ? ` [${resolveSubCommand(ctx)}]` : ""} ${hasOptions(ctx) ? `<${ctx.translation("OPTIONS")}>` : ""} `;
|
|
53
|
-
messages.push(defaultCommand.padStart(ctx.env.leftMargin + defaultCommand.length));
|
|
54
|
-
if (await hasCommands(ctx)) {
|
|
55
|
-
const commandsUsage = `${resolveEntry(ctx)} <${ctx.translation("COMMANDS")}>`;
|
|
56
|
-
messages.push(commandsUsage.padStart(ctx.env.leftMargin + commandsUsage.length));
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
const usageStr = `${resolveEntry(ctx)} ${resolveSubCommand(ctx)} ${generateOptionsSymbols(ctx)}`;
|
|
60
|
-
messages.push(usageStr.padStart(ctx.env.leftMargin + usageStr.length));
|
|
61
|
-
}
|
|
62
|
-
return messages;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Render the commands section
|
|
66
|
-
* @param ctx A {@link CommandContext | command context}
|
|
67
|
-
* @returns A rendered commands section
|
|
68
|
-
*/
|
|
69
|
-
async function renderCommandsSection(ctx) {
|
|
70
|
-
const messages = [`${ctx.translation("COMMANDS")}:`];
|
|
71
|
-
const loadedCommands = await ctx.loadCommands();
|
|
72
|
-
const commandMaxLength = Math.max(...loadedCommands.map((cmd) => (cmd.name || "").length));
|
|
73
|
-
const commandsStr = await Promise.all(loadedCommands.map((cmd) => {
|
|
74
|
-
const key = cmd.name || "";
|
|
75
|
-
const desc = cmd.description || "";
|
|
76
|
-
const command = `${key.padEnd(commandMaxLength + ctx.env.middleMargin)}${desc} `;
|
|
77
|
-
return `${command.padStart(ctx.env.leftMargin + command.length)} `;
|
|
78
|
-
}));
|
|
79
|
-
messages.push(...commandsStr, "", ctx.translation("FORMORE"));
|
|
80
|
-
messages.push(...loadedCommands.map((cmd) => {
|
|
81
|
-
const commandHelp = `${ctx.env.name} ${cmd.name} --help`;
|
|
82
|
-
return `${commandHelp.padStart(ctx.env.leftMargin + commandHelp.length)}`;
|
|
83
|
-
}));
|
|
84
|
-
return messages;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Resolve the entry command name
|
|
88
|
-
* @param ctx A {@link CommandContext | command context}
|
|
89
|
-
* @returns The entry command name
|
|
90
|
-
*/
|
|
91
|
-
function resolveEntry(ctx) {
|
|
92
|
-
return ctx.env.name || ctx.translation("COMMAND");
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Resolve the sub command name
|
|
96
|
-
* @param ctx A {@link CommandContext | command context}
|
|
97
|
-
* @returns The sub command name
|
|
98
|
-
*/
|
|
99
|
-
function resolveSubCommand(ctx) {
|
|
100
|
-
return ctx.name || ctx.translation("SUBCOMMAND");
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Check if the command has a description
|
|
104
|
-
* @param ctx A {@link CommandContext | command context}
|
|
105
|
-
* @returns True if the command has a description
|
|
106
|
-
*/
|
|
107
|
-
function hasDescription(ctx) {
|
|
108
|
-
return !!ctx.description;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Check if the command has sub commands
|
|
112
|
-
* @param ctx A {@link CommandContext | command context}
|
|
113
|
-
* @returns True if the command has sub commands
|
|
114
|
-
*/
|
|
115
|
-
async function hasCommands(ctx) {
|
|
116
|
-
const loadedCommands = await ctx.loadCommands();
|
|
117
|
-
return loadedCommands.length > 1;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Check if the command has options
|
|
121
|
-
* @param ctx A {@link CommandContext | command context}
|
|
122
|
-
* @returns True if the command has options
|
|
123
|
-
*/
|
|
124
|
-
function hasOptions(ctx) {
|
|
125
|
-
return !!(ctx.options && Object.keys(ctx.options).length > 0);
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Check if the command has examples
|
|
129
|
-
* @param ctx A {@link CommandContext | command context}
|
|
130
|
-
* @returns True if the command has examples
|
|
131
|
-
*/
|
|
132
|
-
function hasExamples(ctx) {
|
|
133
|
-
return !!ctx.usage.examples;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Check if all options have default values
|
|
137
|
-
* @param ctx A {@link CommandContext | command context}
|
|
138
|
-
* @returns True if all options have default values
|
|
139
|
-
*/
|
|
140
|
-
function hasAllDefaultOptions(ctx) {
|
|
141
|
-
return !!(ctx.options && Object.values(ctx.options).every((opt) => opt.default));
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Generate options symbols for usage
|
|
145
|
-
* @param ctx A {@link CommandContext | command context}
|
|
146
|
-
* @returns Options symbols for usage
|
|
147
|
-
*/
|
|
148
|
-
function generateOptionsSymbols(ctx) {
|
|
149
|
-
return hasOptions(ctx) ? hasAllDefaultOptions(ctx) ? `[${ctx.translation("OPTIONS")}]` : `<${ctx.translation("OPTIONS")}>` : "";
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Get options pairs for usage
|
|
153
|
-
* @param ctx A {@link CommandContext | command context}
|
|
154
|
-
* @returns Options pairs for usage
|
|
155
|
-
*/
|
|
156
|
-
function getOptionsPairs(ctx) {
|
|
157
|
-
return Object.entries(ctx.options).reduce((acc, [name, value]) => {
|
|
158
|
-
let key = `--${name}`;
|
|
159
|
-
if (value.short) key = `-${value.short}, ${key}`;
|
|
160
|
-
if (value.type !== "boolean") key = value.default ? `${key} [${name}]` : `${key} <${name}>`;
|
|
161
|
-
acc[name] = key;
|
|
162
|
-
return acc;
|
|
163
|
-
}, create());
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Generate options usage
|
|
167
|
-
* @param ctx A {@link CommandContext | command context}
|
|
168
|
-
* @param optionsPairs Options pairs for usage
|
|
169
|
-
* @returns Generated options usage
|
|
170
|
-
*/
|
|
171
|
-
async function generateOptionsUsage(ctx, optionsPairs) {
|
|
172
|
-
const optionsMaxLength = Math.max(...Object.entries(optionsPairs).map(([_, value]) => value.length));
|
|
173
|
-
const optionSchemaMaxLength = ctx.env.usageOptionType ? Math.max(...Object.entries(optionsPairs).map(([key, _]) => ctx.options[key].type.length)) : 0;
|
|
174
|
-
const usages = await Promise.all(Object.entries(optionsPairs).map(([key, value]) => {
|
|
175
|
-
const rawDesc = ctx.translation(key);
|
|
176
|
-
const optionsSchema = ctx.env.usageOptionType ? `[${ctx.options[key].type}] ` : "";
|
|
177
|
-
const desc = `${optionsSchema ? optionsSchema.padEnd(optionSchemaMaxLength + 3) : ""}${rawDesc}`;
|
|
178
|
-
const option = `${value.padEnd(optionsMaxLength + ctx.env.middleMargin)}${desc}`;
|
|
179
|
-
return `${option.padStart(ctx.env.leftMargin + option.length)}`;
|
|
180
|
-
}));
|
|
181
|
-
return usages.join("\n");
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
//#endregion
|
|
185
|
-
//#region src/renderer/validation.ts
|
|
186
|
-
function renderValidationErrors(_ctx, error) {
|
|
187
|
-
const messages = [];
|
|
188
|
-
for (const err of error.errors) messages.push(err.message);
|
|
189
|
-
return Promise.resolve(messages.join("\n"));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
//#endregion
|
|
193
6
|
//#region src/cli.ts
|
|
194
7
|
async function cli(args, entry, opts = {}) {
|
|
195
8
|
const tokens = parseArgs(args);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ArgOptions } from 'args-tokens';
|
|
2
|
+
import { b as CommandContext } from '../types.d-CxaX4FVV.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Render the header
|
|
6
|
+
* @param ctx A {@link CommandContext | command context}
|
|
7
|
+
* @returns A rendered header
|
|
8
|
+
*/
|
|
9
|
+
declare function renderHeader<Options extends ArgOptions = ArgOptions>(ctx: Readonly<CommandContext<Options>>): Promise<string>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Render the usage
|
|
13
|
+
* @param ctx A {@link CommandContext | command context}
|
|
14
|
+
* @returns A rendered usage
|
|
15
|
+
*/
|
|
16
|
+
declare function renderUsage<Options extends ArgOptions = ArgOptions>(ctx: Readonly<CommandContext<Options>>): Promise<string>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Render the validation errors
|
|
20
|
+
* @param ctx A {@link CommandContext | command context}
|
|
21
|
+
* @param error An {@link AggregateError} of option in `args-token` validation
|
|
22
|
+
* @returns A rendered validation error
|
|
23
|
+
*/
|
|
24
|
+
declare function renderValidationErrors<Options extends ArgOptions = ArgOptions>(_ctx: CommandContext<Options>, error: AggregateError): Promise<string>;
|
|
25
|
+
|
|
26
|
+
export { renderHeader, renderUsage, renderValidationErrors };
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { create } from "./utils-NHs5DuHk.js";
|
|
2
|
+
|
|
3
|
+
//#region src/renderer/header.ts
|
|
4
|
+
function renderHeader(ctx) {
|
|
5
|
+
const title = ctx.env.description || ctx.env.name || "";
|
|
6
|
+
return Promise.resolve(title ? `${title} (${ctx.env.name || ""}${ctx.env.version ? ` v${ctx.env.version}` : ""})` : title);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/renderer/usage.ts
|
|
11
|
+
async function renderUsage(ctx) {
|
|
12
|
+
const messages = [];
|
|
13
|
+
if (!ctx.omitted && hasDescription(ctx)) messages.push(ctx.description, "");
|
|
14
|
+
messages.push(...await renderUsageSection(ctx), "");
|
|
15
|
+
if (ctx.omitted && await hasCommands(ctx)) messages.push(...await renderCommandsSection(ctx), "");
|
|
16
|
+
if (hasOptions(ctx)) messages.push(...await renderOptionsSection(ctx), "");
|
|
17
|
+
if (hasExamples(ctx)) messages.push(...renderExamplesSection(ctx), "");
|
|
18
|
+
return messages.join("\n");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Render the options section
|
|
22
|
+
* @param ctx A {@link CommandContext | command context}
|
|
23
|
+
* @returns A rendered options section
|
|
24
|
+
*/
|
|
25
|
+
async function renderOptionsSection(ctx) {
|
|
26
|
+
const messages = [];
|
|
27
|
+
messages.push(`${ctx.translation("OPTIONS")}:`);
|
|
28
|
+
const optionsPairs = getOptionsPairs(ctx);
|
|
29
|
+
messages.push(await generateOptionsUsage(ctx, optionsPairs));
|
|
30
|
+
return messages;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Render the examples section
|
|
34
|
+
* @param ctx A {@link CommandContext | command context}
|
|
35
|
+
* @returns A rendered examples section
|
|
36
|
+
*/
|
|
37
|
+
function renderExamplesSection(ctx) {
|
|
38
|
+
const messages = [];
|
|
39
|
+
const examples = ctx.usage.examples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
|
|
40
|
+
messages.push(`${ctx.translation("EXAMPLES")}:`, ...examples);
|
|
41
|
+
return messages;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Render the usage section
|
|
45
|
+
* @param ctx A {@link CommandContext | command context}
|
|
46
|
+
* @returns A rendered usage section
|
|
47
|
+
*/
|
|
48
|
+
async function renderUsageSection(ctx) {
|
|
49
|
+
const messages = [`${ctx.translation("USAGE")}:`];
|
|
50
|
+
if (ctx.omitted) {
|
|
51
|
+
const defaultCommand = `${resolveEntry(ctx)}${await hasCommands(ctx) ? ` [${resolveSubCommand(ctx)}]` : ""} ${hasOptions(ctx) ? `<${ctx.translation("OPTIONS")}>` : ""} `;
|
|
52
|
+
messages.push(defaultCommand.padStart(ctx.env.leftMargin + defaultCommand.length));
|
|
53
|
+
if (await hasCommands(ctx)) {
|
|
54
|
+
const commandsUsage = `${resolveEntry(ctx)} <${ctx.translation("COMMANDS")}>`;
|
|
55
|
+
messages.push(commandsUsage.padStart(ctx.env.leftMargin + commandsUsage.length));
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
const usageStr = `${resolveEntry(ctx)} ${resolveSubCommand(ctx)} ${generateOptionsSymbols(ctx)}`;
|
|
59
|
+
messages.push(usageStr.padStart(ctx.env.leftMargin + usageStr.length));
|
|
60
|
+
}
|
|
61
|
+
return messages;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Render the commands section
|
|
65
|
+
* @param ctx A {@link CommandContext | command context}
|
|
66
|
+
* @returns A rendered commands section
|
|
67
|
+
*/
|
|
68
|
+
async function renderCommandsSection(ctx) {
|
|
69
|
+
const messages = [`${ctx.translation("COMMANDS")}:`];
|
|
70
|
+
const loadedCommands = await ctx.loadCommands();
|
|
71
|
+
const commandMaxLength = Math.max(...loadedCommands.map((cmd) => (cmd.name || "").length));
|
|
72
|
+
const commandsStr = await Promise.all(loadedCommands.map((cmd) => {
|
|
73
|
+
const key = cmd.name || "";
|
|
74
|
+
const desc = cmd.description || "";
|
|
75
|
+
const command = `${key.padEnd(commandMaxLength + ctx.env.middleMargin)}${desc} `;
|
|
76
|
+
return `${command.padStart(ctx.env.leftMargin + command.length)} `;
|
|
77
|
+
}));
|
|
78
|
+
messages.push(...commandsStr, "", ctx.translation("FORMORE"));
|
|
79
|
+
messages.push(...loadedCommands.map((cmd) => {
|
|
80
|
+
const commandHelp = `${ctx.env.name} ${cmd.name} --help`;
|
|
81
|
+
return `${commandHelp.padStart(ctx.env.leftMargin + commandHelp.length)}`;
|
|
82
|
+
}));
|
|
83
|
+
return messages;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Resolve the entry command name
|
|
87
|
+
* @param ctx A {@link CommandContext | command context}
|
|
88
|
+
* @returns The entry command name
|
|
89
|
+
*/
|
|
90
|
+
function resolveEntry(ctx) {
|
|
91
|
+
return ctx.env.name || ctx.translation("COMMAND");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve the sub command name
|
|
95
|
+
* @param ctx A {@link CommandContext | command context}
|
|
96
|
+
* @returns The sub command name
|
|
97
|
+
*/
|
|
98
|
+
function resolveSubCommand(ctx) {
|
|
99
|
+
return ctx.name || ctx.translation("SUBCOMMAND");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if the command has a description
|
|
103
|
+
* @param ctx A {@link CommandContext | command context}
|
|
104
|
+
* @returns True if the command has a description
|
|
105
|
+
*/
|
|
106
|
+
function hasDescription(ctx) {
|
|
107
|
+
return !!ctx.description;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check if the command has sub commands
|
|
111
|
+
* @param ctx A {@link CommandContext | command context}
|
|
112
|
+
* @returns True if the command has sub commands
|
|
113
|
+
*/
|
|
114
|
+
async function hasCommands(ctx) {
|
|
115
|
+
const loadedCommands = await ctx.loadCommands();
|
|
116
|
+
return loadedCommands.length > 1;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Check if the command has options
|
|
120
|
+
* @param ctx A {@link CommandContext | command context}
|
|
121
|
+
* @returns True if the command has options
|
|
122
|
+
*/
|
|
123
|
+
function hasOptions(ctx) {
|
|
124
|
+
return !!(ctx.options && Object.keys(ctx.options).length > 0);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if the command has examples
|
|
128
|
+
* @param ctx A {@link CommandContext | command context}
|
|
129
|
+
* @returns True if the command has examples
|
|
130
|
+
*/
|
|
131
|
+
function hasExamples(ctx) {
|
|
132
|
+
return !!ctx.usage.examples;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if all options have default values
|
|
136
|
+
* @param ctx A {@link CommandContext | command context}
|
|
137
|
+
* @returns True if all options have default values
|
|
138
|
+
*/
|
|
139
|
+
function hasAllDefaultOptions(ctx) {
|
|
140
|
+
return !!(ctx.options && Object.values(ctx.options).every((opt) => opt.default));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Generate options symbols for usage
|
|
144
|
+
* @param ctx A {@link CommandContext | command context}
|
|
145
|
+
* @returns Options symbols for usage
|
|
146
|
+
*/
|
|
147
|
+
function generateOptionsSymbols(ctx) {
|
|
148
|
+
return hasOptions(ctx) ? hasAllDefaultOptions(ctx) ? `[${ctx.translation("OPTIONS")}]` : `<${ctx.translation("OPTIONS")}>` : "";
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get options pairs for usage
|
|
152
|
+
* @param ctx A {@link CommandContext | command context}
|
|
153
|
+
* @returns Options pairs for usage
|
|
154
|
+
*/
|
|
155
|
+
function getOptionsPairs(ctx) {
|
|
156
|
+
return Object.entries(ctx.options).reduce((acc, [name, value]) => {
|
|
157
|
+
let key = `--${name}`;
|
|
158
|
+
if (value.short) key = `-${value.short}, ${key}`;
|
|
159
|
+
if (value.type !== "boolean") key = value.default ? `${key} [${name}]` : `${key} <${name}>`;
|
|
160
|
+
acc[name] = key;
|
|
161
|
+
return acc;
|
|
162
|
+
}, create());
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Generate options usage
|
|
166
|
+
* @param ctx A {@link CommandContext | command context}
|
|
167
|
+
* @param optionsPairs Options pairs for usage
|
|
168
|
+
* @returns Generated options usage
|
|
169
|
+
*/
|
|
170
|
+
async function generateOptionsUsage(ctx, optionsPairs) {
|
|
171
|
+
const optionsMaxLength = Math.max(...Object.entries(optionsPairs).map(([_, value]) => value.length));
|
|
172
|
+
const optionSchemaMaxLength = ctx.env.usageOptionType ? Math.max(...Object.entries(optionsPairs).map(([key, _]) => ctx.options[key].type.length)) : 0;
|
|
173
|
+
const usages = await Promise.all(Object.entries(optionsPairs).map(([key, value]) => {
|
|
174
|
+
const rawDesc = ctx.translation(key);
|
|
175
|
+
const optionsSchema = ctx.env.usageOptionType ? `[${ctx.options[key].type}] ` : "";
|
|
176
|
+
const desc = `${optionsSchema ? optionsSchema.padEnd(optionSchemaMaxLength + 3) : ""}${rawDesc}`;
|
|
177
|
+
const option = `${value.padEnd(optionsMaxLength + ctx.env.middleMargin)}${desc}`;
|
|
178
|
+
return `${option.padStart(ctx.env.leftMargin + option.length)}`;
|
|
179
|
+
}));
|
|
180
|
+
return usages.join("\n");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/renderer/validation.ts
|
|
185
|
+
function renderValidationErrors(_ctx, error) {
|
|
186
|
+
const messages = [];
|
|
187
|
+
for (const err of error.errors) messages.push(err.message);
|
|
188
|
+
return Promise.resolve(messages.join("\n"));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//#endregion
|
|
192
|
+
export { renderHeader, renderUsage, renderValidationErrors };
|
|
@@ -88,7 +88,7 @@ interface CommandEnvironment<Options extends ArgOptions = ArgOptions> {
|
|
|
88
88
|
* Sub commands
|
|
89
89
|
* @see {@link CommandOptions.subCommands}
|
|
90
90
|
*/
|
|
91
|
-
subCommands: Map<string, Command<
|
|
91
|
+
subCommands: Map<string, Command<any> | LazyCommand<any>> | undefined;
|
|
92
92
|
/**
|
|
93
93
|
* Render function the command usage
|
|
94
94
|
*/
|
|
@@ -105,7 +105,7 @@ interface CommandEnvironment<Options extends ArgOptions = ArgOptions> {
|
|
|
105
105
|
/**
|
|
106
106
|
* Command options
|
|
107
107
|
*/
|
|
108
|
-
interface CommandOptions<Options extends ArgOptions> {
|
|
108
|
+
interface CommandOptions<Options extends ArgOptions = ArgOptions> {
|
|
109
109
|
/**
|
|
110
110
|
* Current working directory
|
|
111
111
|
*/
|
|
@@ -130,7 +130,7 @@ interface CommandOptions<Options extends ArgOptions> {
|
|
|
130
130
|
/**
|
|
131
131
|
* Sub commands
|
|
132
132
|
*/
|
|
133
|
-
subCommands?: Map<string, Command<
|
|
133
|
+
subCommands?: Map<string, Command<any> | LazyCommand<any>>;
|
|
134
134
|
/**
|
|
135
135
|
* Left margin of the command output
|
|
136
136
|
*/
|
|
@@ -161,7 +161,7 @@ interface CommandOptions<Options extends ArgOptions> {
|
|
|
161
161
|
* @description Command context is the context of the command execution
|
|
162
162
|
*/
|
|
163
163
|
interface CommandContext<
|
|
164
|
-
Options extends ArgOptions,
|
|
164
|
+
Options extends ArgOptions = ArgOptions,
|
|
165
165
|
Values = ArgValues<Options>
|
|
166
166
|
> {
|
|
167
167
|
/**
|
|
@@ -227,7 +227,7 @@ interface CommandContext<
|
|
|
227
227
|
/**
|
|
228
228
|
* Command usage
|
|
229
229
|
*/
|
|
230
|
-
interface CommandUsage<Options extends ArgOptions> {
|
|
230
|
+
interface CommandUsage<Options extends ArgOptions = ArgOptions> {
|
|
231
231
|
/**
|
|
232
232
|
* Options usage
|
|
233
233
|
*/
|
|
@@ -240,7 +240,7 @@ interface CommandUsage<Options extends ArgOptions> {
|
|
|
240
240
|
/**
|
|
241
241
|
* Command interface
|
|
242
242
|
*/
|
|
243
|
-
interface Command<Options extends ArgOptions> {
|
|
243
|
+
interface Command<Options extends ArgOptions = ArgOptions> {
|
|
244
244
|
/**
|
|
245
245
|
* Command name
|
|
246
246
|
* @description
|
|
@@ -282,7 +282,7 @@ interface Command<Options extends ArgOptions> {
|
|
|
282
282
|
* Command resource
|
|
283
283
|
* @experimental
|
|
284
284
|
*/
|
|
285
|
-
interface CommandResource<Options extends ArgOptions> {
|
|
285
|
+
interface CommandResource<Options extends ArgOptions = ArgOptions> {
|
|
286
286
|
/**
|
|
287
287
|
* Command description
|
|
288
288
|
*/
|
|
@@ -302,16 +302,20 @@ interface CommandResource<Options extends ArgOptions> {
|
|
|
302
302
|
* @returns A fetched {@link CommandResource | command resource}
|
|
303
303
|
* @experimental
|
|
304
304
|
*/
|
|
305
|
-
type CommandResourceFetcher<Options extends ArgOptions> = (ctx: Readonly<CommandContext<Options>>) => Promise<CommandResource<Options>>;
|
|
305
|
+
type CommandResourceFetcher<Options extends ArgOptions = ArgOptions> = (ctx: Readonly<CommandContext<Options>>) => Promise<CommandResource<Options>>;
|
|
306
306
|
/**
|
|
307
307
|
* Command runner
|
|
308
308
|
* @param ctx A {@link CommandContext | command context}
|
|
309
309
|
*/
|
|
310
|
-
type CommandRunner<Options extends ArgOptions> = (ctx: Readonly<CommandContext<Options>>) => Awaitable<void>;
|
|
310
|
+
type CommandRunner<Options extends ArgOptions = ArgOptions> = (ctx: Readonly<CommandContext<Options>>) => Awaitable<void>;
|
|
311
311
|
/**
|
|
312
312
|
* Lazy command interface
|
|
313
313
|
* @description lazy command that's not loaded until it is executed
|
|
314
314
|
*/
|
|
315
|
-
type LazyCommand<Options extends ArgOptions> = () => Awaitable<Command<Options>>;
|
|
315
|
+
type LazyCommand<Options extends ArgOptions = ArgOptions> = () => Awaitable<Command<Options>>;
|
|
316
|
+
/**
|
|
317
|
+
* Define a command type
|
|
318
|
+
*/
|
|
319
|
+
type Commandable<Options extends ArgOptions> = Command<Options> | LazyCommand<Options>;
|
|
316
320
|
|
|
317
|
-
export type { Command as C, LazyCommand as L, CommandOptions as a, CommandContext as b, CommandRunner as c, CommandBuiltinOptionsKeys as d, CommandBuiltinResourceKeys as e, CommandBuiltinKeys as f, CommandEnvironment as g, CommandResource as h, CommandResourceFetcher as i };
|
|
321
|
+
export type { Command as C, LazyCommand as L, CommandOptions as a, CommandContext as b, CommandRunner as c, CommandBuiltinOptionsKeys as d, CommandBuiltinResourceKeys as e, CommandBuiltinKeys as f, CommandEnvironment as g, CommandResource as h, CommandResourceFetcher as i, Commandable as j };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/utils.ts
|
|
3
|
+
async function resolveLazyCommand(cmd, name, entry = false) {
|
|
4
|
+
const resolved = Object.assign(create(), typeof cmd == "function" ? await cmd() : cmd, { default: entry });
|
|
5
|
+
if (resolved.name == null && name) resolved.name = name;
|
|
6
|
+
return deepFreeze(resolved);
|
|
7
|
+
}
|
|
8
|
+
function create(obj = null) {
|
|
9
|
+
return Object.create(obj);
|
|
10
|
+
}
|
|
11
|
+
function log(...args) {
|
|
12
|
+
console.log(...args);
|
|
13
|
+
}
|
|
14
|
+
function deepFreeze(obj) {
|
|
15
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
16
|
+
for (const key of Object.keys(obj)) {
|
|
17
|
+
const value = obj[key];
|
|
18
|
+
if (typeof value === "object" && value !== null) deepFreeze(value);
|
|
19
|
+
}
|
|
20
|
+
return Object.freeze(obj);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { create, deepFreeze, log, resolveLazyCommand };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gunshi",
|
|
3
3
|
"description": "Modern javascript command-line library",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.4",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "kazuya kawaguchi",
|
|
7
7
|
"email": "kawakazu80@gmail.com"
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"default": "./lib/context.js"
|
|
50
50
|
},
|
|
51
51
|
"./renderer": {
|
|
52
|
-
"types": "./lib/renderer.d.ts",
|
|
53
|
-
"import": "./lib/renderer.js",
|
|
54
|
-
"require": "./lib/renderer.js",
|
|
55
|
-
"default": "./lib/renderer.js"
|
|
52
|
+
"types": "./lib/renderer/index.d.ts",
|
|
53
|
+
"import": "./lib/renderer/index.js",
|
|
54
|
+
"require": "./lib/renderer/index.js",
|
|
55
|
+
"default": "./lib/renderer/index.js"
|
|
56
56
|
},
|
|
57
57
|
"./package.json": "./package.json",
|
|
58
58
|
"./*": "./*"
|