gunshi 0.2.1 → 0.3.0

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 CHANGED
@@ -5,22 +5,22 @@
5
5
 
6
6
  [![Version][npm-version-src]][npm-version-href]
7
7
  [![CI][ci-src]][ci-href]
8
+ [![InstallSize][install-size-src]][install-size-src]
8
9
 
9
10
  <!--
10
11
  [![JSR][jsr-src]][jsr-href]
11
- [![InstallSize][install-size-src]][install-size-src]
12
12
  -->
13
13
 
14
14
  Gunshi is a modern javascript command-line library
15
15
 
16
16
  > [!TIP]
17
- > gunshi (軍師) is a position in ancient Japanese samurai battle in which a amurai devised strategies and gave orders. That name is inspired by the word "command.”
17
+ > gunshi (軍師) is a position in ancient Japanese samurai battle in which a amurai devised strategies and gave orders. That name is inspired by the word "command".
18
18
 
19
19
  ## ✨ Features
20
20
 
21
21
  Gunshi is designed to simplify the creation of modern command-line interfaces:
22
22
 
23
- - 📏 **Minimal**: Run the commands with a minimum API.
23
+ - 📏 **Simple**: Run the commands with a simple API.
24
24
  - 🛡️ **Type Safe**: Arguments parsing and options value resolution type-safely by [args-tokens](https://github.com/kazupon/args-tokens)
25
25
  - ⚙️ **Declarative configuration**: Configure the command modules declaratively.
26
26
  - 🧩 **Composable**: Sub-commands that can be composed with modularized commands.
@@ -40,13 +40,338 @@ pnpm add gunshi
40
40
 
41
41
  ## yarn
42
42
  yarn add gunshi
43
+ ```
44
+
45
+ ## 🚀 Usage
46
+
47
+ ### Simple API
48
+
49
+ Gunshi has a simple API that is a facade:
50
+
51
+ ```js
52
+ import { cli } from 'gunshi'
53
+
54
+ // Run a simple command
55
+ cli(process.argv.slice(2), () => {
56
+ console.log('Hello from Gunshi!')
57
+ })
58
+ ```
59
+
60
+ ### Type-Safe Arguments
61
+
62
+ Gunshi provides type-safe argument parsing with TypeScript:
63
+
64
+ ```ts
65
+ import { cli } from 'gunshi'
66
+ import type { ArgOptions, Command, CommandContext } from 'gunshi'
67
+
68
+ // Define interfaces for options and values
69
+ interface UserOptions extends ArgOptions {
70
+ name: { type: 'string'; short: 'n' }
71
+ age: { type: 'number'; short: 'a'; default: number }
72
+ verbose: { type: 'boolean'; short: 'v' }
73
+ }
74
+
75
+ interface UserValues {
76
+ name?: string
77
+ age: number
78
+ verbose?: boolean
79
+ }
80
+
81
+ // Create a type-safe command
82
+ const command: Command<UserOptions> = {
83
+ name: 'type-safe',
84
+ options: {
85
+ name: { type: 'string', short: 'n' },
86
+ age: { type: 'number', short: 'a', default: 25 },
87
+ verbose: { type: 'boolean', short: 'v' }
88
+ },
89
+ run: (ctx: CommandContext<UserOptions, UserValues>) => {
90
+ const { name, age, verbose } = ctx.values
91
+ console.log(`Hello, ${name || 'World'}! You are ${age} years old.`)
92
+ }
93
+ }
94
+
95
+ await cli(process.argv.slice(2), command)
96
+ ```
97
+
98
+ For more detailed examples, check out the [playground/type-safe](https://github.com/kazupon/gunshi/tree/main/playground/type-safe) in the repository.
99
+
100
+ ### Declarative Configuration
43
101
 
102
+ Configure commands declaratively:
103
+
104
+ ```js
105
+ import { cli } from 'gunshi'
106
+
107
+ // Define a command with declarative configuration
108
+ const command = {
109
+ name: 'greet',
110
+ description: 'A greeting command',
111
+ options: {
112
+ name: { type: 'string', short: 'n' },
113
+ greeting: { type: 'string', short: 'g', default: 'Hello' },
114
+ times: { type: 'number', short: 't', default: 1 }
115
+ },
116
+ usage: {
117
+ options: {
118
+ name: 'Name to greet',
119
+ greeting: 'Greeting to use (default: "Hello")',
120
+ times: 'Number of times to repeat the greeting (default: 1)'
121
+ }
122
+ },
123
+ run: ctx => {
124
+ const { name = 'World', greeting, times } = ctx.values
125
+ for (let i = 0; i < times; i++) {
126
+ console.log(`${greeting}, ${name}!`)
127
+ }
128
+ }
129
+ }
130
+
131
+ cli(process.argv.slice(2), command, {
132
+ name: 'my-app',
133
+ version: '1.0.0',
134
+ description: 'My CLI application'
135
+ })
44
136
  ```
45
137
 
138
+ For more detailed examples, check out the [playground/declarative](https://github.com/kazupon/gunshi/tree/main/playground/declarative) in the repository.
139
+
140
+ ### Composable Sub-commands
141
+
142
+ Create a CLI with composable sub-commands:
143
+
144
+ ```js
145
+ import { cli } from 'gunshi'
146
+
147
+ // Define sub-commands
148
+ const createCommand = {
149
+ name: 'create',
150
+ description: 'Create a new resource',
151
+ options: {
152
+ name: { type: 'string', short: 'n' }
153
+ },
154
+ run: ctx => {
155
+ console.log(`Creating resource: ${ctx.values.name}`)
156
+ }
157
+ }
158
+
159
+ const listCommand = {
160
+ name: 'list',
161
+ description: 'List all resources',
162
+ run: () => {
163
+ console.log('Listing all resources...')
164
+ }
165
+ }
166
+
167
+ // Create a Map of sub-commands
168
+ const subCommands = new Map()
169
+ subCommands.set('create', createCommand)
170
+ subCommands.set('list', listCommand)
171
+
172
+ // Define the main command
173
+ const mainCommand = {
174
+ name: 'resource-manager',
175
+ description: 'Manage resources',
176
+ run: () => {
177
+ console.log('Use one of the sub-commands: create, list')
178
+ }
179
+ }
180
+
181
+ // Run the CLI with composable sub-commands
182
+ cli(process.argv.slice(2), mainCommand, {
183
+ name: 'my-app',
184
+ version: '1.0.0',
185
+ subCommands
186
+ })
187
+ ```
188
+
189
+ For more detailed examples, check out the [playground/composable](https://github.com/kazupon/gunshi/tree/main/playground/composable) in the repository.
190
+
191
+ ### Lazy & Async Command Loading
192
+
193
+ Load commands lazily and execute them asynchronously:
194
+
195
+ ```js
196
+ import { cli } from 'gunshi'
197
+
198
+ // Define a command that will be loaded lazily
199
+ const lazyCommand = async () => {
200
+ // Simulate async loading
201
+ await new Promise(resolve => setTimeout(resolve, 1000))
202
+
203
+ // Return the actual command
204
+ return {
205
+ name: 'lazy',
206
+ description: 'A command that is loaded lazily',
207
+ run: async ctx => {
208
+ // Async execution
209
+ await new Promise(resolve => setTimeout(resolve, 500))
210
+ console.log('Command executed!')
211
+ }
212
+ }
213
+ }
214
+
215
+ // Create a Map of sub-commands with lazy-loaded commands
216
+ const subCommands = new Map()
217
+ subCommands.set('lazy', lazyCommand)
218
+
219
+ // Run the CLI with lazy-loaded commands
220
+ cli(
221
+ process.argv.slice(2),
222
+ { name: 'main', run: () => {} },
223
+ {
224
+ name: 'my-app',
225
+ subCommands
226
+ }
227
+ )
228
+ ```
229
+
230
+ For more detailed examples, check out the [playground/lazy-async](https://github.com/kazupon/gunshi/tree/main/playground/lazy-async) in the repository.
231
+
232
+ ### Auto Usage Generation
233
+
234
+ Gunshi automatically generates usage information:
235
+
236
+ ```js
237
+ import { cli } from 'gunshi'
238
+
239
+ const command = {
240
+ name: 'app',
241
+ description: 'My application',
242
+ options: {
243
+ path: { type: 'string', short: 'p' },
244
+ recursive: { type: 'boolean', short: 'r' },
245
+ operation: { type: 'string', short: 'o', required: true }
246
+ },
247
+ usage: {
248
+ options: {
249
+ path: 'File or directory path',
250
+ recursive: 'Operate recursively on directories',
251
+ operation: 'Operation to perform (list, copy, move, delete)'
252
+ },
253
+ examples: '# Example\n$ my-app --operation list --path ./src'
254
+ },
255
+ run: ctx => {
256
+ // Command implementation
257
+ }
258
+ }
259
+
260
+ // Run with --help to see the automatically generated usage information
261
+ cli(process.argv.slice(2), command, {
262
+ name: 'my-app',
263
+ version: '1.0.0'
264
+ })
265
+ ```
266
+
267
+ For more detailed examples, check out the [playground/auto-usage](https://github.com/kazupon/gunshi/tree/main/playground/auto-usage) in the repository.
268
+
269
+ ### Custom Usage Generation
270
+
271
+ Customize the usage message generation:
272
+
273
+ ```js
274
+ import { cli } from 'gunshi'
275
+
276
+ // Custom header renderer
277
+ const customHeaderRenderer = ctx => {
278
+ return Promise.resolve(`
279
+ ╔═══════════════════════╗
280
+ ║ ${ctx.env.name.toUpperCase()} ║
281
+ ╚═══════════════════════╝
282
+ ${ctx.env.description}
283
+ Version: ${ctx.env.version}
284
+ `)
285
+ }
286
+
287
+ // Custom usage renderer
288
+ const customUsageRenderer = ctx => {
289
+ const lines = []
290
+ lines.push('USAGE:')
291
+ lines.push(` $ ${ctx.env.name} [options]`)
292
+ lines.push('')
293
+ lines.push('OPTIONS:')
294
+
295
+ for (const [key, option] of Object.entries(ctx.options || {})) {
296
+ const shortFlag = option.short ? `-${option.short}, ` : ' '
297
+ lines.push(` ${shortFlag}--${key.padEnd(10)} ${ctx.translation(key)}`)
298
+ }
299
+
300
+ return Promise.resolve(lines.join('\n'))
301
+ }
302
+
303
+ // Run with custom renderers
304
+ cli(
305
+ process.argv.slice(2),
306
+ { name: 'app', run: () => {} },
307
+ {
308
+ name: 'my-app',
309
+ version: '1.0.0',
310
+ description: 'My application',
311
+ renderHeader: customHeaderRenderer,
312
+ renderUsage: customUsageRenderer
313
+ }
314
+ )
315
+ ```
316
+
317
+ For more detailed examples, check out the [playground/custom-usage](https://github.com/kazupon/gunshi/tree/main/playground/custom-usage) in the repository.
318
+
319
+ ### Internationalization
320
+
321
+ Support internationalization:
322
+
323
+ ```js
324
+ import { cli } from 'gunshi'
325
+ import enUS from './locales/en-US.json' with { type: 'json' }
326
+
327
+ const command = {
328
+ name: 'greeter',
329
+ options: {
330
+ name: { type: 'string', short: 'n' },
331
+ formal: { type: 'boolean', short: 'f' }
332
+ },
333
+ // Resource fetcher for translations
334
+ resource: async ctx => {
335
+ if (ctx.locale.toString() === 'ja-JP') {
336
+ const resource = await import('./locales/ja-JP.json', { with: { type: 'json' } })
337
+ return resource.default
338
+ }
339
+
340
+ // Default to English
341
+ return enUS
342
+ },
343
+ run: ctx => {
344
+ const { name = 'World', formal } = ctx.values
345
+ const greeting = formal ? ctx.translation('formal') : ctx.translation('informal')
346
+ console.log(`${greeting}, ${name}!`)
347
+ }
348
+ }
349
+
350
+ // Run with locale support
351
+ cli(process.argv.slice(2), command, {
352
+ name: 'my-app',
353
+ version: '1.0.0',
354
+ // Set the locale via an environment variable
355
+ // If Node v21 or later is used, you can use the built-in `navigator.language` instead)
356
+ locale: new Intl.Locale(process.env.MY_LOCALE || 'en-US')
357
+ })
358
+ ```
359
+
360
+ For more detailed examples, check out the [playground/i18n](https://github.com/kazupon/gunshi/tree/main/playground/i18n) in the repository.
361
+
46
362
  ## 🙌 Contributing guidelines
47
363
 
48
364
  If you are interested in contributing to `gunshi`, I highly recommend checking out [the contributing guidelines](/CONTRIBUTING.md) here. You'll find all the relevant information such as [how to make a PR](/CONTRIBUTING.md#pull-request-guidelines), [how to setup development](/CONTRIBUTING.md#development-setup)) etc., there.
49
365
 
366
+ ## 💖 Credits
367
+
368
+ This project is inspired by:
369
+
370
+ - [`citty`](https://github.com/unjs/citty), created by UnJS team and contributors
371
+ - cline and claude 3.7 sonnet, examples and docs is generated
372
+
373
+ Thank you!
374
+
50
375
  ## ©️ License
51
376
 
52
377
  [MIT](http://opensource.org/licenses/MIT)
@@ -57,11 +382,7 @@ If you are interested in contributing to `gunshi`, I highly recommend checking o
57
382
  [npm-version-href]: https://npmjs.com/package/gunshi
58
383
  [jsr-src]: https://jsr.io/badges/@kazupon/gunishi
59
384
  [jsr-href]: https://jsr.io/@kazupon/gunshi
60
-
61
- <!--
62
- [install-size-src]: https://pkg-size.dev/badge/install/35082
63
- [install-size-href]: https://pkg-size.dev/gunishi
64
- -->
65
-
385
+ [install-size-src]: https://pkg-size.dev/badge/install/72346
386
+ [install-size-href]: https://pkg-size.dev/gunshi
66
387
  [ci-src]: https://github.com/kazupon/gunshi/actions/workflows/ci.yml/badge.svg
67
388
  [ci-href]: https://github.com/kazupon/gunshi/actions/workflows/ci.yml
@@ -1,4 +1,3 @@
1
- import { create, deepFreeze, resolveLazyCommand } from "./utils-NHs5DuHk.js";
2
1
 
3
2
  //#region locales/en-US.json
4
3
  var COMMAND = "COMMAND";
@@ -57,6 +56,28 @@ const COMMAND_I18N_RESOURCE_KEYS = [
57
56
  "FORMORE"
58
57
  ];
59
58
 
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
+
60
81
  //#endregion
61
82
  //#region src/context.ts
62
83
  const DEFAULT_LOCALE = "en-US";
@@ -95,7 +116,8 @@ async function createCommandContext({ options, values, positionals, command, com
95
116
  localeResources.set(locale.toString(), builtInLoadedResources);
96
117
  } catch {}
97
118
  /**
98
- * define the translation function
119
+ * define the translation function, which is used to {@link CommandContext.translation}.
120
+ *
99
121
  */
100
122
  function translation(key) {
101
123
  if (COMMAND_I18N_RESOURCE_KEYS.includes(key)) {
@@ -174,4 +196,4 @@ async function loadCommandResource(ctx, command) {
174
196
  }
175
197
 
176
198
  //#endregion
177
- export { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, DEFAULT_LOCALE, createCommandContext };
199
+ export { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, DEFAULT_LOCALE, create, createCommandContext, log, resolveLazyCommand };
package/lib/context.d.ts CHANGED
@@ -1,17 +1,50 @@
1
1
  import { ArgOptions, ArgValues } from 'args-tokens';
2
- import { C as Command, a as CommandOptions, b as CommandContext } from './types.d-B3YGxDV6.js';
2
+ import { C as Command, a as CommandOptions, b as CommandContext } from './types.d-00BVt8hZ.js';
3
3
 
4
+ /**
5
+ * The default locale string, which format is BCP 47 language tag
6
+ */
4
7
  declare const DEFAULT_LOCALE = "en-US";
8
+ /**
9
+ * Parameters of {@link createCommandContext}
10
+ */
11
+ interface CommandContextParams<
12
+ Options extends ArgOptions,
13
+ Values
14
+ > {
15
+ /**
16
+ * An options of target command
17
+ */
18
+ options: Options | undefined;
19
+ /**
20
+ * A values of target command
21
+ */
22
+ values: Values;
23
+ /**
24
+ * A positionals arguments, which passed to the target command
25
+ */
26
+ positionals: string[];
27
+ /**
28
+ * Whether the command is omitted
29
+ */
30
+ omitted: boolean;
31
+ /**
32
+ * A target {@link Command | command}
33
+ */
34
+ command: Command<Options>;
35
+ /**
36
+ * A command options, which is spicialized from `cli` function
37
+ */
38
+ commandOptions: CommandOptions<Options>;
39
+ }
40
+ /**
41
+ * Create a {@link CommandContext | command context}
42
+ * @param param A {@link CommandContextParams | parameters} to create a {@link CommandContext | command context}
43
+ * @returns A {@link CommandContext | command context}, which is readonly
44
+ */
5
45
  declare function createCommandContext<
6
46
  Options extends ArgOptions,
7
47
  Values = ArgValues<Options>
8
- >({ options, values, positionals, command, commandOptions, omitted }: {
9
- options: Options | undefined
10
- values: Values
11
- positionals: string[]
12
- omitted: boolean
13
- command: Command<Options>
14
- commandOptions: CommandOptions<Options>
15
- }): Promise<Readonly<CommandContext<Options, Values>>>;
48
+ >({ options, values, positionals, command, commandOptions, omitted }: CommandContextParams<Options, Values>): Promise<Readonly<CommandContext<Options, Values>>>;
16
49
 
17
50
  export { DEFAULT_LOCALE, createCommandContext };
package/lib/context.js CHANGED
@@ -1,4 +1,3 @@
1
- import { DEFAULT_LOCALE, createCommandContext } from "./context-BQKZW5bg.js";
2
- import "./utils-NHs5DuHk.js";
1
+ import { DEFAULT_LOCALE, createCommandContext } from "./context-B5z7lnoV.js";
3
2
 
4
3
  export { DEFAULT_LOCALE, createCommandContext };
package/lib/index.d.ts CHANGED
@@ -1,13 +1,13 @@
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-B3YGxDV6.js';
4
- export { f as CommandBuiltinKeys, d as CommandBuiltinOptionsKeys, e as CommandBuiltinResourceKeys, b as CommandContext, g as CommandEnvironment, i as CommandResource, j as CommandResourceFetcher, h as CommandUsageRender, L as LazyCommand } from './types.d-B3YGxDV6.js';
3
+ import { C as Command, c as CommandRunner, a as CommandOptions } from './types.d-00BVt8hZ.js';
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-00BVt8hZ.js';
5
5
 
6
6
  /**
7
7
  * Run the command
8
8
  * @param args - command line arguments
9
- * @param entry - a {@link Command | entry command} or an {@link CommandRunner | inline command runner}
10
- * @param opts - a {@link CommandOptions | command options}
9
+ * @param entry - A {@link Command | entry command} or an {@link CommandRunner | inline command runner}
10
+ * @param opts - A {@link CommandOptions | command options}
11
11
  */
12
12
  declare function cli<Options extends ArgOptions>(args: string[], entry: Command<Options> | CommandRunner<Options>, opts?: CommandOptions<Options>): Promise<void>;
13
13
 
package/lib/index.js CHANGED
@@ -1,8 +1,195 @@
1
- import { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, createCommandContext } from "./context-BQKZW5bg.js";
2
- import { create, log, resolveLazyCommand } from "./utils-NHs5DuHk.js";
3
- import { renderHeader, renderUsage, renderValidationErrors } from "./renderer-Bo0DibAK.js";
1
+ import { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, create, createCommandContext, log, resolveLazyCommand } from "./context-B5z7lnoV.js";
4
2
  import { parseArgs, resolveArgs } from "args-tokens";
5
3
 
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
6
193
  //#region src/cli.ts
7
194
  async function cli(args, entry, opts = {}) {
8
195
  const tokens = parseArgs(args);
@@ -33,7 +220,7 @@ async function cli(args, entry, opts = {}) {
33
220
  }
34
221
  if (error) {
35
222
  await showValidationErrors(ctx, error);
36
- throw error;
223
+ return;
37
224
  }
38
225
  await command.run(ctx);
39
226
  }
@@ -25,54 +25,54 @@ declare namespace __constants {
25
25
  */
26
26
  type Awaitable<T> = T | Promise<T>;
27
27
  /**
28
- * The command i18n built-in options keys
28
+ * Command i18n built-in options keys
29
29
  * @experimental
30
30
  */
31
31
  type CommandBuiltinOptionsKeys = keyof (typeof __constants)["COMMON_OPTIONS"];
32
32
  /**
33
- * The command i18n built-in resource keys
33
+ * Command i18n built-in resource keys
34
34
  * @experimental
35
35
  */
36
36
  type CommandBuiltinResourceKeys = (typeof __constants)["COMMAND_I18N_RESOURCE_KEYS"][number];
37
37
  /**
38
- * The command i18n built-in keys
38
+ * Command i18n built-in keys
39
39
  * @description The command i18n built-in keys are used to {@link CommandContext.translation | translate} function
40
40
  * @experimental
41
41
  */
42
42
  type CommandBuiltinKeys = CommandBuiltinOptionsKeys | CommandBuiltinResourceKeys | "description" | "examples";
43
43
  /**
44
- * The command environment
44
+ * Command environment
45
45
  */
46
46
  interface CommandEnvironment<Options extends ArgOptions = ArgOptions> {
47
47
  /**
48
- * The current working directory
48
+ * Current working directory
49
49
  * @see {@link CommandOptions.cwd}
50
50
  */
51
51
  cwd: string | undefined;
52
52
  /**
53
- * The command name
53
+ * Command name
54
54
  * @see {@link CommandOptions.name}
55
55
  */
56
56
  name: string | undefined;
57
57
  /**
58
- * The command description
58
+ * Command description
59
59
  * @see {@link CommandOptions.description}
60
60
  *
61
61
  */
62
62
  description: string | undefined;
63
63
  /**
64
- * The command version
64
+ * Command version
65
65
  * @see {@link CommandOptions.version}
66
66
  */
67
67
  version: string | undefined;
68
68
  /**
69
- * The left margin of the command output
69
+ * Left margin of the command output
70
70
  * @default 2
71
71
  * @see {@link CommandOptions.leftMargin}
72
72
  */
73
73
  leftMargin: number;
74
74
  /**
75
- * The middle margin of the command output
75
+ * Middle margin of the command output
76
76
  * @default 10
77
77
  * @see {@link CommandOptions.middleMargin}
78
78
  */
@@ -84,7 +84,7 @@ interface CommandEnvironment<Options extends ArgOptions = ArgOptions> {
84
84
  */
85
85
  usageOptionType: boolean;
86
86
  /**
87
- * The sub commands
87
+ * Sub commands
88
88
  * @see {@link CommandOptions.subCommands}
89
89
  */
90
90
  subCommands: Map<string, Command<Options> | LazyCommand<Options>> | undefined;
@@ -102,45 +102,40 @@ interface CommandEnvironment<Options extends ArgOptions = ArgOptions> {
102
102
  renderValidationErrors: ((ctx: CommandContext<Options>, error: AggregateError) => Promise<string>) | null | undefined;
103
103
  }
104
104
  /**
105
- * The command options
105
+ * Command options
106
106
  */
107
107
  interface CommandOptions<Options extends ArgOptions> {
108
108
  /**
109
- * The current working directory
110
- * @description This is the current working directory path passed in the context of the run command. This is useful if you need your command about the current execution directory.
109
+ * Current working directory
111
110
  */
112
111
  cwd?: string;
113
112
  /**
114
- * The command name
115
- * @description Please specify the name of the command that was executed. If you would specify it, gunshi will be displayed in the usage.
113
+ * Command program name
116
114
  */
117
115
  name?: string;
118
116
  /**
119
- * The command description
120
- * @description Please specify the description (summary) of the command that was executed. If you would specify it, gunshi will be displayed in the usage.
117
+ * Command program description
121
118
  *
122
119
  */
123
120
  description?: string;
124
121
  /**
125
- * The command version
126
- * @description Please specify the version of the command that was executed. If you would specify it, gunshi will be displayed in the usage.
122
+ * Command program version
127
123
  */
128
124
  version?: string;
129
125
  /**
130
- * The locale of the command
131
- * @description The locale of the command that was executed. If you would specify it, gunshi command usage will be localized.
126
+ * Command program locale
132
127
  */
133
128
  locale?: string | Intl.Locale;
134
129
  /**
135
- * The sub commands
130
+ * Sub commands
136
131
  */
137
132
  subCommands?: Map<string, Command<Options> | LazyCommand<Options>>;
138
133
  /**
139
- * The left margin of the command output
134
+ * Left margin of the command output
140
135
  */
141
136
  leftMargin?: number;
142
137
  /**
143
- * The middle margin of the command output
138
+ * Middle margin of the command output
144
139
  */
145
140
  middleMargin?: number;
146
141
  /**
@@ -161,44 +156,44 @@ interface CommandOptions<Options extends ArgOptions> {
161
156
  renderValidationErrors?: ((ctx: Readonly<CommandContext<Options>>, error: AggregateError) => Promise<string>) | null;
162
157
  }
163
158
  /**
164
- * The command context
165
- * @description The command context is the context of the command execution
159
+ * Command context
160
+ * @description Command context is the context of the command execution
166
161
  */
167
162
  interface CommandContext<
168
163
  Options extends ArgOptions,
169
164
  Values = ArgValues<Options>
170
165
  > {
171
166
  /**
172
- * The command name, that is the command that is executed
167
+ * Command name, that is the command that is executed
173
168
  * @description The command name is same {@link CommandEnvironment.name}
174
169
  */
175
170
  name: string | undefined;
176
171
  /**
177
- * The command description, that is the description of the command that is executed
172
+ * Command description, that is the description of the command that is executed
178
173
  * @description The command description is same {@link CommandEnvironment.description}
179
174
  */
180
175
  description: string | undefined;
181
176
  /**
182
- * The command locale, that is the locale of the command that is executed
177
+ * Command locale, that is the locale of the command that is executed
183
178
  */
184
179
  locale: Intl.Locale;
185
180
  /**
186
- * The command environment, that is the environment of the command that is executed
181
+ * Command environment, that is the environment of the command that is executed
187
182
  * @description The command environment is same {@link CommandEnvironment}
188
183
  */
189
184
  env: CommandEnvironment<Options>;
190
185
  /**
191
- * The command options, that is the options of the command that is executed
186
+ * Command options, that is the options of the command that is executed
192
187
  * @description The command options is same {@link Command.options}
193
188
  */
194
189
  options: Options | undefined;
195
190
  /**
196
- * The command values, that is the values of the command that is executed
191
+ * Command values, that is the values of the command that is executed
197
192
  * @description Resolve values with `resolveArgs` from command arguments and {@link Command.options}
198
193
  */
199
194
  values: Values;
200
195
  /**
201
- * The command positionals, that is the positionals of the command that is executed
196
+ * Command positionals arguments, that is the positionals of the command that is executed
202
197
  * @description Resolve positionals with `resolveArgs` from command arguments
203
198
  */
204
199
  positionals: string[];
@@ -207,20 +202,20 @@ interface CommandContext<
207
202
  */
208
203
  omitted: boolean;
209
204
  /**
210
- * The usage of the command
211
- * @description The usage of the command is same {@link Command.usage}, and more has `--help` and `--version` options
205
+ * Command usage
206
+ * @description Usage of the command is same {@link Command.usage}, and more has `--help` and `--version` options
212
207
  */
213
208
  usage: CommandUsage<Options>;
214
209
  /**
215
- * Load the sub-commands
210
+ * Load sub-commands
216
211
  * @description The loaded commands are cached and returned when called again
217
212
  * @returns loaded commands
218
213
  */
219
214
  loadCommands: () => Promise<Command<Options>[]>;
220
215
  /**
221
- * The translation function
222
- * @param key {CommandBuiltinKeys | T} - The key to be translated
223
- * @returns The translated string, if the key is not found, the key itself is returned
216
+ * Translation function
217
+ * @param key the key to be translated
218
+ * @returns A translated string
224
219
  * @experimental
225
220
  */
226
221
  translation: <
@@ -229,37 +224,32 @@ interface CommandContext<
229
224
  >(key: Key) => string;
230
225
  }
231
226
  /**
232
- * The command usage render
233
- * @description if the render function is async, it should return a promise
234
- */
235
- type CommandUsageRender<Options extends ArgOptions> = ((ctx: Readonly<CommandContext<Options>>) => Promise<string>) | string;
236
- /**
237
- * The command usage
227
+ * Command usage
238
228
  */
239
229
  interface CommandUsage<Options extends ArgOptions> {
240
230
  /**
241
- * The options usage
231
+ * Options usage
242
232
  */
243
233
  options?: { [Option in keyof Options] : string };
244
234
  /**
245
- * The examples usage
235
+ * Examples usage
246
236
  */
247
237
  examples?: string;
248
238
  }
249
239
  /**
250
- * The command interface
240
+ * Command interface
251
241
  */
252
242
  interface Command<Options extends ArgOptions> {
253
243
  /**
254
- * The command name
244
+ * Command name
255
245
  * @description
256
- * The command name is used to find command line arguments to execute from sub commands, so it's recommended to specify.
246
+ * Command name is used to find command line arguments to execute from sub commands, so it's recommended to specify.
257
247
  */
258
248
  name?: string;
259
249
  /**
260
- * The command description
250
+ * Command description
261
251
  * @description
262
- * The command description is used to describe the command in usage, so it's recommended to specify.
252
+ * Command description is used to describe the command in usage, so it's recommended to specify.
263
253
  */
264
254
  description?: string;
265
255
  /**
@@ -268,57 +258,59 @@ interface Command<Options extends ArgOptions> {
268
258
  */
269
259
  default?: boolean;
270
260
  /**
271
- * The command options
261
+ * Command options
272
262
  */
273
263
  options?: Options;
274
264
  /**
275
- * The command usage
265
+ * Command usage
276
266
  * @description
277
- * The command usage is used to describe the command in usage, so it's recommended to specify.
267
+ * Command usage is used to describe the command in usage, so it's recommended to specify.
278
268
  */
279
269
  usage?: CommandUsage<Options>;
280
270
  /**
281
- * The command runner, that's the command to be executed
271
+ * Command runner, that's the command to be executed
282
272
  */
283
273
  run: CommandRunner<Options>;
284
274
  /**
285
- * The command resource fetcher
275
+ * Command resource fetcher
286
276
  * @experimental
287
277
  */
288
278
  resource?: CommandResourceFetcher<Options>;
289
279
  }
290
280
  /**
291
- * The command resource
281
+ * Command resource
292
282
  * @experimental
293
283
  */
294
284
  interface CommandResource<Options extends ArgOptions> {
295
285
  /**
296
- * The command description resource
286
+ * Command description
297
287
  */
298
288
  description: string;
299
289
  /**
300
- * The options usage resources
290
+ * Options usage
301
291
  */
302
292
  options: { [Option in keyof Options] : string };
303
293
  /**
304
- * The examples usage resources
294
+ * Examples usage
305
295
  */
306
296
  examples: string;
307
297
  }
308
298
  /**
309
- * The command resource fetcher
299
+ * Command resource fetcher
300
+ * @param ctx A {@link CommandContext | command context}
301
+ * @returns A fetched {@link CommandResource | command resource}
310
302
  * @experimental
311
303
  */
312
304
  type CommandResourceFetcher<Options extends ArgOptions> = (ctx: Readonly<CommandContext<Options>>) => Promise<CommandResource<Options>>;
313
305
  /**
314
- * The command runner interface
315
- * @param ctx - The {@link CommandContext | command context}
306
+ * Command runner
307
+ * @param ctx A {@link CommandContext | command context}
316
308
  */
317
309
  type CommandRunner<Options extends ArgOptions> = (ctx: Readonly<CommandContext<Options>>) => Awaitable<void>;
318
310
  /**
319
- * The lazy command interface
320
- * @description The lazy command that's not loaded until it is executed
311
+ * Lazy command interface
312
+ * @description lazy command that's not loaded until it is executed
321
313
  */
322
314
  type LazyCommand<Options extends ArgOptions> = () => Awaitable<Command<Options>>;
323
315
 
324
- 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, CommandUsageRender as h, CommandResource as i, CommandResourceFetcher as j };
316
+ 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 };
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.2.1",
4
+ "version": "0.3.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -86,10 +86,10 @@
86
86
  "gh-changelogen": "^0.2.8",
87
87
  "knip": "^5.45.0",
88
88
  "lint-staged": "^15.4.3",
89
- "pkg-pr-new": "^0.0.40",
89
+ "pkg-pr-new": "^0.0.41",
90
90
  "prettier": "^3.5.3",
91
91
  "tsdown": "^0.6.4",
92
- "typescript": "^5.8.2",
92
+ "typescript": "^5.4.2",
93
93
  "typescript-eslint": "^8.26.0",
94
94
  "vitest": "^3.0.7"
95
95
  },
@@ -113,11 +113,11 @@
113
113
  "clean": "git clean -df",
114
114
  "dev": "pnpx @eslint/config-inspector --config eslint.config.ts",
115
115
  "dev:eslint": "pnpx @eslint/config-inspector --config eslint.config.ts",
116
- "fix": "pnpm run --parallel --color \"/^fix:/\"",
116
+ "fix": "pnpm run --stream --color \"/^fix:/\"",
117
117
  "fix:eslint": "eslint . --fix",
118
118
  "fix:knip": "knip --fix --no-exit-code",
119
119
  "fix:prettier": "prettier . --write",
120
- "lint": "pnpm run --parallel --color \"/^lint:/\"",
120
+ "lint": "pnpm run --stream --color \"/^lint:/\"",
121
121
  "lint:eslint": "eslint .",
122
122
  "lint:knip": "knip",
123
123
  "lint:prettier": "prettier . --check",
@@ -1,115 +0,0 @@
1
- import { create } from "./utils-NHs5DuHk.js";
2
-
3
- //#region src/renderer.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
- async function renderUsage(ctx) {
9
- const messages = [];
10
- if (!ctx.omitted && hasDescription(ctx)) messages.push(ctx.description, "");
11
- messages.push(...await renderUsageSection(ctx), "");
12
- if (ctx.omitted && await hasCommands(ctx)) messages.push(...await renderCommandsSection(ctx), "");
13
- if (hasOptions(ctx)) messages.push(...await renderOptionsSection(ctx), "");
14
- if (hasExamples(ctx)) messages.push(...renderExamplesSection(ctx), "");
15
- return messages.join("\n");
16
- }
17
- function renderValidationErrors(_ctx, error) {
18
- const messages = [];
19
- for (const err of error.errors) messages.push(err.message);
20
- return Promise.resolve(messages.join("\n"));
21
- }
22
- async function renderOptionsSection(ctx) {
23
- const messages = [];
24
- messages.push(`${ctx.translation("OPTIONS")}:`);
25
- const optionsPairs = getOptionsPairs(ctx);
26
- messages.push(await generateOptionsUsage(ctx, optionsPairs));
27
- return messages;
28
- }
29
- function renderExamplesSection(ctx) {
30
- const messages = [];
31
- const examples = ctx.usage.examples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
32
- messages.push(`${ctx.translation("EXAMPLES")}:`, ...examples);
33
- return messages;
34
- }
35
- async function renderUsageSection(ctx) {
36
- const messages = [`${ctx.translation("USAGE")}:`];
37
- if (ctx.omitted) {
38
- const defaultCommand = `${resolveEntry(ctx)}${await hasCommands(ctx) ? ` [${resolveSubCommand(ctx)}]` : ""} ${hasOptions(ctx) ? `<${ctx.translation("OPTIONS")}>` : ""} `;
39
- messages.push(defaultCommand.padStart(ctx.env.leftMargin + defaultCommand.length));
40
- if (await hasCommands(ctx)) {
41
- const commandsUsage = `${resolveEntry(ctx)} <${ctx.translation("COMMANDS")}>`;
42
- messages.push(commandsUsage.padStart(ctx.env.leftMargin + commandsUsage.length));
43
- }
44
- } else {
45
- const usageStr = `${resolveEntry(ctx)} ${resolveSubCommand(ctx)} ${generateOptionsSymbols(ctx)}`;
46
- messages.push(usageStr.padStart(ctx.env.leftMargin + usageStr.length));
47
- }
48
- return messages;
49
- }
50
- async function renderCommandsSection(ctx) {
51
- const messages = [`${ctx.translation("COMMANDS")}:`];
52
- const loadedCommands = await ctx.loadCommands();
53
- const commandMaxLength = Math.max(...loadedCommands.map((cmd) => (cmd.name || "").length));
54
- const commandsStr = await Promise.all(loadedCommands.map((cmd) => {
55
- const key = cmd.name || "";
56
- const desc = cmd.description || "";
57
- const command = `${key.padEnd(commandMaxLength + ctx.env.middleMargin)}${desc} `;
58
- return `${command.padStart(ctx.env.leftMargin + command.length)} `;
59
- }));
60
- messages.push(...commandsStr, "", ctx.translation("FORMORE"));
61
- messages.push(...loadedCommands.map((cmd) => {
62
- const commandHelp = `${ctx.env.name} ${cmd.name} --help`;
63
- return `${commandHelp.padStart(ctx.env.leftMargin + commandHelp.length)}`;
64
- }));
65
- return messages;
66
- }
67
- function resolveEntry(ctx) {
68
- return ctx.env.name || ctx.translation("COMMAND");
69
- }
70
- function resolveSubCommand(ctx) {
71
- return ctx.name || ctx.translation("SUBCOMMAND");
72
- }
73
- function hasDescription(ctx) {
74
- return !!ctx.description;
75
- }
76
- async function hasCommands(ctx) {
77
- const loadedCommands = await ctx.loadCommands();
78
- return loadedCommands.length > 1;
79
- }
80
- function hasOptions(ctx) {
81
- return !!(ctx.options && Object.keys(ctx.options).length > 0);
82
- }
83
- function hasExamples(ctx) {
84
- return !!ctx.usage.examples;
85
- }
86
- function hasAllDefaultOptions(ctx) {
87
- return !!(ctx.options && Object.values(ctx.options).every((opt) => opt.default));
88
- }
89
- function generateOptionsSymbols(ctx) {
90
- return hasOptions(ctx) ? hasAllDefaultOptions(ctx) ? `[${ctx.translation("OPTIONS")}]` : `<${ctx.translation("OPTIONS")}>` : "";
91
- }
92
- function getOptionsPairs(ctx) {
93
- return Object.entries(ctx.options).reduce((acc, [name, value]) => {
94
- let key = `--${name}`;
95
- if (value.short) key = `-${value.short}, ${key}`;
96
- if (value.type !== "boolean") key = value.default ? `${key} [${name}]` : `${key} <${name}>`;
97
- acc[name] = key;
98
- return acc;
99
- }, create());
100
- }
101
- async function generateOptionsUsage(ctx, optionsPairs) {
102
- const optionsMaxLength = Math.max(...Object.entries(optionsPairs).map(([_, value]) => value.length));
103
- const optionSchemaMaxLength = ctx.env.usageOptionType ? Math.max(...Object.entries(optionsPairs).map(([key, _]) => ctx.options[key].type.length)) : 0;
104
- const usages = await Promise.all(Object.entries(optionsPairs).map(([key, value]) => {
105
- const rawDesc = ctx.translation(key);
106
- const optionsSchema = ctx.env.usageOptionType ? `[${ctx.options[key].type}] ` : "";
107
- const desc = `${optionsSchema ? optionsSchema.padEnd(optionSchemaMaxLength + 3) : ""}${rawDesc}`;
108
- const option = `${value.padEnd(optionsMaxLength + ctx.env.middleMargin)}${desc}`;
109
- return `${option.padStart(ctx.env.leftMargin + option.length)}`;
110
- }));
111
- return usages.join("\n");
112
- }
113
-
114
- //#endregion
115
- export { renderHeader, renderUsage, renderValidationErrors };
package/lib/renderer.d.ts DELETED
@@ -1,8 +0,0 @@
1
- import { ArgOptions } from 'args-tokens';
2
- import { b as CommandContext } from './types.d-B3YGxDV6.js';
3
-
4
- declare function renderHeader<Options extends ArgOptions>(ctx: Readonly<CommandContext<Options>>): Promise<string>;
5
- declare function renderUsage<Options extends ArgOptions>(ctx: Readonly<CommandContext<Options>>): Promise<string>;
6
- declare function renderValidationErrors<Options extends ArgOptions>(_ctx: CommandContext<Options>, error: AggregateError): Promise<string>;
7
-
8
- export { renderHeader, renderUsage, renderValidationErrors };
package/lib/renderer.js DELETED
@@ -1,4 +0,0 @@
1
- import "./utils-NHs5DuHk.js";
2
- import { renderHeader, renderUsage, renderValidationErrors } from "./renderer-Bo0DibAK.js";
3
-
4
- export { renderHeader, renderUsage, renderValidationErrors };
@@ -1,24 +0,0 @@
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 };