gunshi 0.9.0 → 0.10.1

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
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img width="196" src="./assets/logo.webp">
2
+ <img width="196" src="./assets/logo.png">
3
3
  </p>
4
4
  <h1 align="center">🏯 Gunshi</h1>
5
5
 
@@ -10,21 +10,25 @@
10
10
 
11
11
  Gunshi is a modern javascript command-line library
12
12
 
13
- > [!TIP] <!-- eslint-disable-line markdown/no-missing-label-refs -->
13
+ <!-- eslint-disable markdown/no-missing-label-refs -->
14
+
15
+ > [!TIP]
14
16
  > gunshi (軍師) is a position in ancient Japanese samurai battle in which a samurai devised strategies and gave orders. That name is inspired by the word "command".
15
17
 
18
+ <!-- eslint-enable markdown/no-missing-label-refs -->
19
+
16
20
  ## ✨ Features
17
21
 
18
22
  Gunshi is designed to simplify the creation of modern command-line interfaces:
19
23
 
20
- - 📏 **Simple**: Run the commands with a simple API.
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
- - 🧩 **Composable**: Sub-commands that can be composed with modularized commands.
24
- - ⏳ **Lazy & Async**: Command modules lazy loading and asynchronously executing.
25
- - 📜 **Auto usage generation**: Automatic usage message generation with modularized commands.
26
- - 🎨 **Custom usage generation**: Usage message generation customizable.
27
- - 🌍 **Internationalization**: I18n out of the box and locale resource lazy loading.
24
+ - 📏 **Simple & Universal**: Run the commands with simple API and support universal runtime.
25
+ - ⚙️ **Declarative configuration**: Configure command modules declaratively for better organization and maintainability.
26
+ - 🛡️ **Type Safe**: TypeScript support with type-safe argument parsing and option resolution by [args-tokens](https://github.com/kazupon/args-tokens)
27
+ - 🧩 **Composable**: Create modular sub-commands that can be composed together for complex CLIs.
28
+ - ⏳ **Lazy & Async**: Load command modules lazily and execute them asynchronously for better performance.
29
+ - 📜 **Auto usage generation**: Generate helpful usage messages automatically for your commands.
30
+ - 🎨 **Custom usage generation**: Customize how usage messages are generated to match your CLI's style.
31
+ - 🌍 **Internationalization**: Support multiple languages with built-in i18n, locale resource lazy loading and i18n library integration.
28
32
 
29
33
  ## 💿 Installation
30
34
 
@@ -82,15 +86,22 @@ const command = {
82
86
  name: 'greet',
83
87
  description: 'A greeting command',
84
88
  options: {
85
- name: { type: 'string', short: 'n' },
86
- greeting: { type: 'string', short: 'g', default: 'Hello' },
87
- times: { type: 'number', short: 't', default: 1 }
88
- },
89
- usage: {
90
- options: {
91
- name: 'Name to greet',
92
- greeting: 'Greeting to use (default: "Hello")',
93
- times: 'Number of times to repeat the greeting (default: 1)'
89
+ name: {
90
+ type: 'string',
91
+ short: 'n',
92
+ description: 'Name to greet'
93
+ },
94
+ greeting: {
95
+ type: 'string',
96
+ short: 'g',
97
+ default: 'Hello',
98
+ description: 'Greeting to use (default: "Hello")'
99
+ },
100
+ times: {
101
+ type: 'number',
102
+ short: 't',
103
+ default: 1,
104
+ description: 'Number of times to repeat the greeting (default: 1)'
94
105
  }
95
106
  },
96
107
  run: ctx => {
@@ -262,19 +273,25 @@ const command = {
262
273
  name: 'app',
263
274
  description: 'My application',
264
275
  options: {
265
- path: { type: 'string', short: 'p' },
266
- recursive: { type: 'boolean', short: 'r' },
267
- operation: { type: 'string', short: 'o', required: true }
268
- },
269
- // define usage with object
270
- usage: {
271
- options: {
272
- path: 'File or directory path',
273
- recursive: 'Operate recursively on directories',
274
- operation: 'Operation to perform (list, copy, move, delete)'
276
+ path: {
277
+ type: 'string',
278
+ short: 'p',
279
+ description: 'File or directory path'
280
+ },
281
+ recursive: {
282
+ type: 'boolean',
283
+ short: 'r',
284
+ description: 'Operate recursively on directories'
275
285
  },
276
- examples: '# Example\n$ my-app --operation list --path ./src'
286
+ operation: {
287
+ type: 'string',
288
+ short: 'o',
289
+ required: true,
290
+ description: 'Operation to perform (list, copy, move, delete)'
291
+ }
277
292
  },
293
+ // define examples
294
+ examples: '# Example\n$ my-app --operation list --path ./src',
278
295
  run: ctx => {
279
296
  // command implementation
280
297
  }
@@ -350,8 +367,14 @@ import enUS from './locales/en-US.json' with { type: 'json' }
350
367
  const command = {
351
368
  name: 'greeter',
352
369
  options: {
353
- name: { type: 'string', short: 'n' },
354
- formal: { type: 'boolean', short: 'f' }
370
+ name: {
371
+ type: 'string',
372
+ short: 'n'
373
+ },
374
+ formal: {
375
+ type: 'boolean',
376
+ short: 'f'
377
+ }
355
378
  },
356
379
  // resource fetcher for translations
357
380
  resource: async ctx => {
@@ -0,0 +1,96 @@
1
+ import { createCommandContext } from "./context-BeFe0i70.js";
2
+ import { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, create, resolveLazyCommand } from "./utils-DhI1qcqR.js";
3
+ import { renderHeader, renderUsage, renderValidationErrors } from "./renderer-CZGhbue4.js";
4
+ import { parseArgs, resolveArgs } from "args-tokens";
5
+
6
+ //#region src/cli.ts
7
+ async function cli(args, entry, opts = {}) {
8
+ const tokens = parseArgs(args);
9
+ const subCommand = getSubCommand(tokens);
10
+ const resolvedCommandOptions = resolveCommandOptions(opts, entry);
11
+ const [name, command] = await resolveCommand(subCommand, entry, resolvedCommandOptions);
12
+ if (!command) throw new Error(`Command not found: ${name || ""}`);
13
+ const options = resolveArgOptions(command.options);
14
+ const { values, positionals, error } = resolveArgs(options, tokens);
15
+ const omitted = !subCommand;
16
+ const ctx = await createCommandContext({
17
+ options,
18
+ values,
19
+ positionals,
20
+ omitted,
21
+ command,
22
+ commandOptions: resolvedCommandOptions
23
+ });
24
+ if (values.version) {
25
+ showVersion(ctx);
26
+ return;
27
+ }
28
+ const usageBuffer = [];
29
+ const header = await showHeader(ctx);
30
+ if (header) usageBuffer.push(header);
31
+ if (values.help) {
32
+ const usage = await showUsage(ctx);
33
+ if (usage) usageBuffer.push(usage);
34
+ return usageBuffer.join("\n");
35
+ }
36
+ if (error) {
37
+ await showValidationErrors(ctx, error);
38
+ return;
39
+ }
40
+ await command.run(ctx);
41
+ }
42
+ function resolveArgOptions(options) {
43
+ return Object.assign(create(), options, COMMON_OPTIONS);
44
+ }
45
+ function resolveCommandOptions(options, entry) {
46
+ const subCommands = new Map(options.subCommands);
47
+ if (typeof entry === "object" && entry.name) subCommands.set(entry.name, entry);
48
+ const resolvedOptions = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, options, { subCommands });
49
+ return resolvedOptions;
50
+ }
51
+ function getSubCommand(tokens) {
52
+ const firstToken = tokens[0];
53
+ return firstToken && firstToken.kind === "positional" && firstToken.index === 0 && firstToken.value ? firstToken.value : "";
54
+ }
55
+ async function showUsage(ctx) {
56
+ if (ctx.env.renderUsage === null) return;
57
+ const usage = await (ctx.env.renderUsage || renderUsage)(ctx);
58
+ if (usage) {
59
+ ctx.log(usage);
60
+ return usage;
61
+ }
62
+ }
63
+ function showVersion(ctx) {
64
+ ctx.log(ctx.env.version);
65
+ }
66
+ async function showHeader(ctx) {
67
+ if (ctx.env.renderHeader === null) return;
68
+ const header = await (ctx.env.renderHeader || renderHeader)(ctx);
69
+ if (header) {
70
+ ctx.log(header);
71
+ ctx.log();
72
+ return header;
73
+ }
74
+ }
75
+ async function showValidationErrors(ctx, error) {
76
+ if (ctx.env.renderValidationErrors === null) return;
77
+ const render = ctx.env.renderValidationErrors || renderValidationErrors;
78
+ ctx.log(await render(ctx, error));
79
+ }
80
+ async function resolveCommand(sub, entry, options) {
81
+ const omitted = !sub;
82
+ if (typeof entry === "function") return [void 0, {
83
+ run: entry,
84
+ default: true
85
+ }];
86
+ else if (omitted) return typeof entry === "object" ? [entry.name, await resolveLazyCommand(entry, void 0, true)] : [void 0, void 0];
87
+ else {
88
+ if (options.subCommands == null) return [sub, void 0];
89
+ const cmd = options.subCommands?.get(sub);
90
+ if (cmd == null) return [sub, void 0];
91
+ return [sub, await resolveLazyCommand(cmd, sub)];
92
+ }
93
+ }
94
+
95
+ //#endregion
96
+ export { cli };
@@ -1,4 +1,4 @@
1
- import { BUILT_IN_PREFIX, COMMAND_OPTIONS_DEFAULT, DEFAULT_LOCALE, NOOP, create, deepFreeze, log, mapResourceWithBuiltinKey, resolveLazyCommand } from "./utils-jm146hfy.js";
1
+ import { BUILT_IN_PREFIX, COMMAND_OPTIONS_DEFAULT, DEFAULT_LOCALE, NOOP, create, deepFreeze, log, mapResourceWithBuiltinKey, resolveLazyCommand } from "./utils-DhI1qcqR.js";
2
2
 
3
3
  //#region locales/en-US.json
4
4
  var COMMAND = "COMMAND";
@@ -61,30 +61,21 @@ var DefaultTranslation = class {
61
61
  const BUILT_IN_PREFIX_CODE = BUILT_IN_PREFIX.codePointAt(0);
62
62
  async function createCommandContext({ options, values, positionals, command, commandOptions, omitted = false }) {
63
63
  /**
64
- * tweak the options and values
64
+ * normailize the options schema and values, to avoid prototype pollution
65
65
  */
66
66
  const _options = Object.entries(options).reduce((acc, [key, value]) => {
67
67
  acc[key] = Object.assign(create(), value);
68
68
  return acc;
69
69
  }, create());
70
- const _values = Object.assign(create(), values);
71
- /**
72
- * normalize the usage
73
- */
74
- const usage = Object.assign(create(), command.usage);
75
- const { help: help$1, version: version$1 } = en_US_default;
76
- usage.options = Object.assign(create(), usage.options, {
77
- help: help$1,
78
- version: version$1
79
- });
80
70
  /**
81
71
  * setup the environment
82
72
  */
83
73
  const env = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, commandOptions);
84
74
  const locale = resolveLocale(commandOptions.locale);
75
+ const localeStr = locale.toString();
85
76
  const translationAdapterFactory = commandOptions.translationAdapterFactory || createTranslationAdapter;
86
77
  const adapter = translationAdapterFactory({
87
- locale: locale.toString(),
78
+ locale: localeStr,
88
79
  fallbackLocale: DEFAULT_LOCALE
89
80
  });
90
81
  const localeResources = new Map();
@@ -93,9 +84,9 @@ async function createCommandContext({ options, values, positionals, command, com
93
84
  * load the built-in locale resources
94
85
  */
95
86
  localeResources.set(DEFAULT_LOCALE, mapResourceWithBuiltinKey(en_US_default));
96
- if (DEFAULT_LOCALE !== locale.toString()) try {
97
- builtInLoadedResources = await import(`../locales/${locale.toString()}.json`, { with: { type: "json" } });
98
- localeResources.set(locale.toString(), mapResourceWithBuiltinKey(builtInLoadedResources));
87
+ if (DEFAULT_LOCALE !== localeStr) try {
88
+ builtInLoadedResources = await import(`../locales/${localeStr}.json`, { with: { type: "json" } });
89
+ localeResources.set(localeStr, mapResourceWithBuiltinKey(builtInLoadedResources));
99
90
  } catch {}
100
91
  /**
101
92
  * define the translation function, which is used to {@link CommandContext.translate}.
@@ -104,7 +95,7 @@ async function createCommandContext({ options, values, positionals, command, com
104
95
  function translate(key, values$1 = create()) {
105
96
  const strKey = key;
106
97
  if (strKey.codePointAt(0) === BUILT_IN_PREFIX_CODE) {
107
- const resource = localeResources.get(locale.toString()) || localeResources.get(DEFAULT_LOCALE);
98
+ const resource = localeResources.get(localeStr) || localeResources.get(DEFAULT_LOCALE);
108
99
  return resource[strKey] || strKey;
109
100
  } else return adapter.translate(locale.toString(), strKey, values$1) || "";
110
101
  }
@@ -114,7 +105,7 @@ async function createCommandContext({ options, values, positionals, command, com
114
105
  let cachedCommands;
115
106
  async function loadCommands() {
116
107
  if (cachedCommands) return cachedCommands;
117
- const subCommands = [...env.subCommands || []];
108
+ const subCommands = [...commandOptions.subCommands || []];
118
109
  return cachedCommands = await Promise.all(subCommands.map(async ([name, cmd]) => await resolveLazyCommand(cmd, name)));
119
110
  }
120
111
  /**
@@ -127,9 +118,8 @@ async function createCommandContext({ options, values, positionals, command, com
127
118
  locale,
128
119
  env,
129
120
  options: _options,
130
- values: _values,
121
+ values,
131
122
  positionals,
132
- usage,
133
123
  log: commandOptions.usageSilent ? NOOP : log,
134
124
  loadCommands,
135
125
  translate
@@ -137,16 +127,16 @@ async function createCommandContext({ options, values, positionals, command, com
137
127
  /**
138
128
  * load the command resources
139
129
  */
140
- const loadedOptionsResources = Object.entries(usage.options || create()).map(([key, _]) => {
141
- const option = usage.options[key];
142
- return [key, option];
130
+ const loadedOptionsResources = Object.entries(options).map(([key, option]) => {
131
+ const description = option.description || "";
132
+ return [key, description];
143
133
  });
144
134
  const defaultCommandResource = loadedOptionsResources.reduce((res, [key, value]) => {
145
135
  res[key] = value;
146
136
  return res;
147
137
  }, create());
148
138
  defaultCommandResource.description = command.description || "";
149
- defaultCommandResource.examples = usage.examples || "";
139
+ defaultCommandResource.examples = command.examples || "";
150
140
  adapter.setResource(DEFAULT_LOCALE, defaultCommandResource);
151
141
  const originalResource = await loadCommandResource(ctx, command);
152
142
  if (originalResource) {
@@ -158,7 +148,7 @@ async function createCommandContext({ options, values, positionals, command, com
158
148
  resource.help = builtInLoadedResources.help;
159
149
  resource.version = builtInLoadedResources.version;
160
150
  }
161
- adapter.setResource(locale.toString(), resource);
151
+ adapter.setResource(localeStr, resource);
162
152
  }
163
153
  return ctx;
164
154
  }
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-Dp0YJQNw.js';
2
+ import { C as Command, a as CommandOptions, b as CommandContext } from './types.d-DTe4N67v.js';
3
3
 
4
4
  /**
5
5
  * Parameters of {@link createCommandContext}
@@ -40,7 +40,7 @@ interface CommandContextParams<
40
40
  */
41
41
  declare function createCommandContext<
42
42
  Options extends ArgOptions = ArgOptions,
43
- Values = ArgValues<Options>
43
+ Values extends ArgValues<Options> = ArgValues<Options>
44
44
  >({ options, values, positionals, command, commandOptions, omitted }: CommandContextParams<Options, Values>): Promise<Readonly<CommandContext<Options, Values>>>;
45
45
 
46
46
  export { createCommandContext };
package/lib/context.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createCommandContext } from "./context-DeTM_Qpg.js";
2
- import "./utils-jm146hfy.js";
1
+ import { createCommandContext } from "./context-BeFe0i70.js";
2
+ import "./utils-DhI1qcqR.js";
3
3
 
4
4
  export { createCommandContext };
@@ -0,0 +1,13 @@
1
+ import { ArgOptions } from 'args-tokens';
2
+ import { C as Command, c as CommandRunner, a as CommandOptions } from './types.d-DTe4N67v.js';
3
+
4
+ /**
5
+ * Generate the command usage
6
+ * @param command - usage generate command, if you want to generate the usage of the default command where there are target commands and sub-commands, specify `null`.
7
+ * @param entry - A {@link Command | entry command} or an {@link CommandRunner | inline command runner}
8
+ * @param opts - A {@link CommandOptions | command options}
9
+ * @returns A rendered usage
10
+ */
11
+ declare function generate<Options extends ArgOptions = ArgOptions>(command: string | null, entry: Command<Options> | CommandRunner<Options>, opts?: CommandOptions<Options>): Promise<string>;
12
+
13
+ export { generate };
@@ -0,0 +1,17 @@
1
+ import "./context-BeFe0i70.js";
2
+ import { create } from "./utils-DhI1qcqR.js";
3
+ import "./renderer-CZGhbue4.js";
4
+ import { cli } from "./cli-BmK-tBjI.js";
5
+
6
+ //#region src/generator.ts
7
+ async function generate(command, entry, opts = {}) {
8
+ const args = ["-h"];
9
+ if (command != null) args.unshift(command);
10
+ return await cli(args, entry, Object.assign(create(), opts, {
11
+ usageSilent: true,
12
+ __proto__: null
13
+ })) || "";
14
+ }
15
+
16
+ //#endregion
17
+ export { generate };
package/lib/index.d.ts CHANGED
@@ -1,13 +1,14 @@
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, T as TranslationAdapter, d as TranslationAdapterFactoryOptions } from './types.d-Dp0YJQNw.js';
4
- export { g as CommandBuiltinKeys, e as CommandBuiltinOptionsKeys, f as CommandBuiltinResourceKeys, b as CommandContext, h as CommandEnvironment, i as CommandResource, j as CommandResourceFetcher, l as Commandable, G as GenerateNamespacedKey, L as LazyCommand, k as TranslationAdapterFactory } from './types.d-Dp0YJQNw.js';
3
+ import { C as Command, c as CommandRunner, a as CommandOptions, T as TranslationAdapter, d as TranslationAdapterFactoryOptions } from './types.d-DTe4N67v.js';
4
+ export { g as CommandBuiltinKeys, e as CommandBuiltinOptionsKeys, f as CommandBuiltinResourceKeys, b as CommandContext, h as CommandEnvironment, i as CommandResource, j as CommandResourceFetcher, l as Commandable, G as GenerateNamespacedKey, L as LazyCommand, k as TranslationAdapterFactory } from './types.d-DTe4N67v.js';
5
5
 
6
6
  /**
7
7
  * Run the command
8
8
  * @param args - command line arguments
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
+ * @returns A rendered usage or undefined. if you will use {@link CommandOptions.usageSilent} option, it will return rendered usage string.
11
12
  */
12
13
  declare function cli<Options extends ArgOptions = ArgOptions>(args: string[], entry: Command<Options> | CommandRunner<Options>, opts?: CommandOptions<Options>): Promise<string | undefined>;
13
14
 
package/lib/index.js CHANGED
@@ -1,96 +1,6 @@
1
- import { DefaultTranslation, createCommandContext } from "./context-DeTM_Qpg.js";
2
- import { COMMAND_OPTIONS_DEFAULT, COMMON_OPTIONS, create, resolveLazyCommand } from "./utils-jm146hfy.js";
3
- import { renderHeader, renderUsage, renderValidationErrors } from "./renderer-DIRwkoRd.js";
4
- import { parseArgs, resolveArgs } from "args-tokens";
1
+ import { DefaultTranslation } from "./context-BeFe0i70.js";
2
+ import "./utils-DhI1qcqR.js";
3
+ import "./renderer-CZGhbue4.js";
4
+ import { cli } from "./cli-BmK-tBjI.js";
5
5
 
6
- //#region src/cli.ts
7
- async function cli(args, entry, opts = {}) {
8
- const tokens = parseArgs(args);
9
- const subCommand = getSubCommand(tokens);
10
- const resolvedCommandOptions = resolveCommandOptions(opts, entry);
11
- const [name, command] = await resolveCommand(subCommand, entry, resolvedCommandOptions);
12
- if (!command) throw new Error(`Command not found: ${name || ""}`);
13
- const options = resolveArgOptions(command.options);
14
- const { values, positionals, error } = resolveArgs(options, tokens);
15
- const omitted = !subCommand;
16
- const ctx = await createCommandContext({
17
- options,
18
- values,
19
- positionals,
20
- omitted,
21
- command,
22
- commandOptions: resolvedCommandOptions
23
- });
24
- if (values.version) {
25
- showVersion(ctx);
26
- return;
27
- }
28
- const usageBuffer = [];
29
- const header = await showHeader(ctx);
30
- if (header) usageBuffer.push(header);
31
- if (values.help) {
32
- const usage = await showUsage(ctx);
33
- if (usage) usageBuffer.push(usage);
34
- return usageBuffer.join("\n");
35
- }
36
- if (error) {
37
- await showValidationErrors(ctx, error);
38
- return;
39
- }
40
- await command.run(ctx);
41
- }
42
- function resolveArgOptions(options) {
43
- return Object.assign(create(), options, COMMON_OPTIONS);
44
- }
45
- function resolveCommandOptions(options, entry) {
46
- const subCommands = new Map(options.subCommands);
47
- if (typeof entry === "object" && entry.name) subCommands.set(entry.name, entry);
48
- const resolvedOptions = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, options, { subCommands });
49
- return resolvedOptions;
50
- }
51
- function getSubCommand(tokens) {
52
- const firstToken = tokens[0];
53
- return firstToken && firstToken.kind === "positional" && firstToken.index === 0 && firstToken.value ? firstToken.value : "";
54
- }
55
- async function showUsage(ctx) {
56
- if (ctx.env.renderUsage === null) return;
57
- const usage = await (ctx.env.renderUsage || renderUsage)(ctx);
58
- if (usage) {
59
- ctx.log(usage);
60
- return usage;
61
- }
62
- }
63
- function showVersion(ctx) {
64
- ctx.log(ctx.env.version);
65
- }
66
- async function showHeader(ctx) {
67
- if (ctx.env.renderHeader === null) return;
68
- const header = await (ctx.env.renderHeader || renderHeader)(ctx);
69
- if (header) {
70
- ctx.log(header);
71
- ctx.log();
72
- return header;
73
- }
74
- }
75
- async function showValidationErrors(ctx, error) {
76
- if (ctx.env.renderValidationErrors === null) return;
77
- const render = ctx.env.renderValidationErrors || renderValidationErrors;
78
- ctx.log(await render(ctx, error));
79
- }
80
- async function resolveCommand(sub, entry, options) {
81
- const omitted = !sub;
82
- if (typeof entry === "function") return [void 0, {
83
- run: entry,
84
- default: true
85
- }];
86
- else if (omitted) return typeof entry === "object" ? [entry.name, await resolveLazyCommand(entry, void 0, true)] : [void 0, void 0];
87
- else {
88
- if (options.subCommands == null) return [sub, void 0];
89
- const cmd = options.subCommands?.get(sub);
90
- if (cmd == null) return [sub, void 0];
91
- return [sub, await resolveLazyCommand(cmd, sub)];
92
- }
93
- }
94
-
95
- //#endregion
96
6
  export { DefaultTranslation, cli };
@@ -1,5 +1,5 @@
1
1
  import { ArgOptions } from 'args-tokens';
2
- import { b as CommandContext } from '../types.d-Dp0YJQNw.js';
2
+ import { b as CommandContext } from '../types.d-DTe4N67v.js';
3
3
 
4
4
  /**
5
5
  * Render the header
@@ -1,4 +1,4 @@
1
- import "../utils-jm146hfy.js";
2
- import { renderHeader, renderUsage, renderValidationErrors } from "../renderer-DIRwkoRd.js";
1
+ import "../utils-DhI1qcqR.js";
2
+ import { renderHeader, renderUsage, renderValidationErrors } from "../renderer-CZGhbue4.js";
3
3
 
4
4
  export { renderHeader, renderUsage, renderValidationErrors };
@@ -1,4 +1,4 @@
1
- import { create, resolveBuiltInKey } from "./utils-jm146hfy.js";
1
+ import { create, resolveBuiltInKey } from "./utils-DhI1qcqR.js";
2
2
 
3
3
  //#region src/renderer/header.ts
4
4
  function renderHeader(ctx) {
@@ -17,7 +17,8 @@ async function renderUsage(ctx) {
17
17
  messages.push(...await renderUsageSection(ctx), "");
18
18
  if (ctx.omitted && await hasCommands(ctx)) messages.push(...await renderCommandsSection(ctx), "");
19
19
  if (hasOptions(ctx)) messages.push(...await renderOptionsSection(ctx), "");
20
- if (hasExamples(ctx)) messages.push(...renderExamplesSection(ctx), "");
20
+ const examples = renderExamplesSection(ctx);
21
+ if (examples.length > 0) messages.push(...examples, "");
21
22
  return messages.join("\n");
22
23
  }
23
24
  /**
@@ -38,8 +39,11 @@ async function renderOptionsSection(ctx) {
38
39
  */
39
40
  function renderExamplesSection(ctx) {
40
41
  const messages = [];
41
- const examples = ctx.usage.examples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
42
- messages.push(`${ctx.translate(resolveBuiltInKey("EXAMPLES"))}:`, ...examples);
42
+ const resolvedExamples = resolveExamples(ctx);
43
+ if (resolvedExamples) {
44
+ const examples = resolvedExamples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
45
+ messages.push(`${ctx.translate(resolveBuiltInKey("EXAMPLES"))}:`, ...examples);
46
+ }
43
47
  return messages;
44
48
  }
45
49
  /**
@@ -109,6 +113,17 @@ function resolveDescription(ctx) {
109
113
  return ctx.translate("description") || ctx.description || "";
110
114
  }
111
115
  /**
116
+ * Resolve the command examples
117
+ * @param ctx A {@link CommandContext | command context}
118
+ * @returns resolved command examples, if not resolved, return empty string
119
+ */
120
+ function resolveExamples(ctx) {
121
+ const ret = ctx.translate("examples");
122
+ if (ret) return ret;
123
+ const command = ctx.env.subCommands?.get(ctx.name || "");
124
+ return command?.examples ?? "";
125
+ }
126
+ /**
112
127
  * Check if the command has sub commands
113
128
  * @param ctx A {@link CommandContext | command context}
114
129
  * @returns True if the command has sub commands
@@ -126,14 +141,6 @@ function hasOptions(ctx) {
126
141
  return !!(ctx.options && Object.keys(ctx.options).length > 0);
127
142
  }
128
143
  /**
129
- * Check if the command has examples
130
- * @param ctx A {@link CommandContext | command context}
131
- * @returns True if the command has examples
132
- */
133
- function hasExamples(ctx) {
134
- return !!ctx.usage.examples;
135
- }
136
- /**
137
144
  * Check if all options have default values
138
145
  * @param ctx A {@link CommandContext | command context}
139
146
  * @returns True if all options have default values
@@ -147,7 +154,7 @@ function hasAllDefaultOptions(ctx) {
147
154
  * @returns Options symbols for usage
148
155
  */
149
156
  function generateOptionsSymbols(ctx) {
150
- return hasOptions(ctx) ? hasAllDefaultOptions(ctx) ? `[${ctx.translate("_:OPTIONS")}]` : `<${ctx.translate("_:OPTIONS")}>` : "";
157
+ return hasOptions(ctx) ? hasAllDefaultOptions(ctx) ? `[${ctx.translate(resolveBuiltInKey("OPTIONS"))}]` : `<${ctx.translate(resolveBuiltInKey("OPTIONS"))}>` : "";
151
158
  }
152
159
  /**
153
160
  * Get options pairs for usage
@@ -11,10 +11,12 @@ type CommonOptionType = {
11
11
  readonly help: {
12
12
  readonly type: "boolean"
13
13
  readonly short: "h"
14
+ readonly description: string
14
15
  }
15
16
  readonly version: {
16
17
  readonly type: "boolean"
17
18
  readonly short: "v"
19
+ readonly description: string
18
20
  }
19
21
  };
20
22
  declare const COMMON_OPTIONS: CommonOptionType;
@@ -212,7 +214,7 @@ interface CommandContext<
212
214
  * Command environment, that is the environment of the command that is executed
213
215
  * @description The command environment is same {@link CommandEnvironment}
214
216
  */
215
- env: CommandEnvironment<Options>;
217
+ env: Readonly<CommandEnvironment<Options>>;
216
218
  /**
217
219
  * Command options, that is the options of the command that is executed
218
220
  * @description The command options is same {@link Command.options}
@@ -233,11 +235,6 @@ interface CommandContext<
233
235
  */
234
236
  omitted: boolean;
235
237
  /**
236
- * Command usage
237
- * @description Usage of the command is same {@link Command.usage}, and more has `--help` and `--version` options
238
- */
239
- usage: CommandUsage<Options>;
240
- /**
241
238
  * Output a message
242
239
  * @description if {@link CommandEnvironment.usageSilent} is true, the message is not output
243
240
  * @param message an output message, @see {@link console.log}
@@ -263,19 +260,6 @@ interface CommandContext<
263
260
  >(key: Key, values?: Record<string, unknown>) => string;
264
261
  }
265
262
  /**
266
- * Command usage
267
- */
268
- interface CommandUsage<Options extends ArgOptions = ArgOptions> {
269
- /**
270
- * Options usage
271
- */
272
- options?: { [Option in keyof Options] : string };
273
- /**
274
- * Examples usage
275
- */
276
- examples?: string;
277
- }
278
- /**
279
263
  * Command interface
280
264
  */
281
265
  interface Command<Options extends ArgOptions = ArgOptions> {
@@ -287,8 +271,7 @@ interface Command<Options extends ArgOptions = ArgOptions> {
287
271
  name?: string;
288
272
  /**
289
273
  * Command description
290
- * @description
291
- * Command description is used to describe the command in usage, so it's recommended to specify.
274
+ * @description command description is used to describe the command in usage, so it's recommended to specify.
292
275
  */
293
276
  description?: string;
294
277
  /**
@@ -298,14 +281,14 @@ interface Command<Options extends ArgOptions = ArgOptions> {
298
281
  default?: boolean;
299
282
  /**
300
283
  * Command options
284
+ * @description each option can include a description property to describe the option in usage.
301
285
  */
302
286
  options?: Options;
303
287
  /**
304
- * Command usage
305
- * @description
306
- * Command usage is used to describe the command in usage, so it's recommended to specify.
288
+ * Command examples
289
+ * @description examples of how to use the command.
307
290
  */
308
- usage?: CommandUsage<Options>;
291
+ examples?: string;
309
292
  /**
310
293
  * Command runner, that's the command to be executed
311
294
  */
@@ -338,7 +321,10 @@ type CommandResource<Options extends ArgOptions = ArgOptions> = {
338
321
  * @returns A fetched {@link CommandResource | command resource}
339
322
  * @experimental
340
323
  */
341
- type CommandResourceFetcher<Options extends ArgOptions = ArgOptions> = (ctx: Readonly<CommandContext<Options>>) => Promise<CommandResource<Options>>;
324
+ type CommandResourceFetcher<
325
+ Options extends ArgOptions = ArgOptions,
326
+ Values = ArgValues<Options>
327
+ > = (ctx: Readonly<CommandContext<Options, Values>>) => Promise<CommandResource<Options>>;
342
328
  /**
343
329
  * Translation adapter factory
344
330
  */
@@ -7,11 +7,13 @@ const NOOP = () => {};
7
7
  const COMMON_OPTIONS = {
8
8
  help: {
9
9
  type: "boolean",
10
- short: "h"
10
+ short: "h",
11
+ description: "Display this help message"
11
12
  },
12
13
  version: {
13
14
  type: "boolean",
14
- short: "v"
15
+ short: "v",
16
+ description: "Display this version"
15
17
  }
16
18
  };
17
19
  const COMMAND_OPTIONS_DEFAULT = {
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.9.0",
4
+ "version": "0.10.1",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -54,6 +54,12 @@
54
54
  "require": "./lib/renderer/index.js",
55
55
  "default": "./lib/renderer/index.js"
56
56
  },
57
+ "./generator": {
58
+ "types": "./lib/generator.d.ts",
59
+ "import": "./lib/generator.js",
60
+ "require": "./lib/generator.js",
61
+ "default": "./lib/generator.js"
62
+ },
57
63
  "./package.json": "./package.json",
58
64
  "./*": "./*"
59
65
  },
@@ -67,7 +73,7 @@
67
73
  }
68
74
  },
69
75
  "dependencies": {
70
- "args-tokens": "^0.10.2"
76
+ "args-tokens": "^0.12.0"
71
77
  },
72
78
  "devDependencies": {
73
79
  "@eslint/markdown": "^6.2.2",
@@ -85,7 +91,7 @@
85
91
  "eslint-plugin-module-interop": "^0.3.0",
86
92
  "eslint-plugin-promise": "^7.2.1",
87
93
  "eslint-plugin-regexp": "^2.7.0",
88
- "eslint-plugin-unicorn": "^57.0.0",
94
+ "eslint-plugin-unicorn": "^58.0.0",
89
95
  "eslint-plugin-unused-imports": "^4.1.4",
90
96
  "eslint-plugin-yml": "^1.17.0",
91
97
  "gh-changelogen": "^0.2.8",
@@ -99,6 +105,7 @@
99
105
  "typescript": "^5.4.2",
100
106
  "typescript-eslint": "^8.26.0",
101
107
  "vitepress": "^1.6.3",
108
+ "vitepress-plugin-group-icons": "^1.3.8",
102
109
  "vitest": "^3.0.7"
103
110
  },
104
111
  "prettier": "@kazupon/prettier-config",