@gunshi/bone 0.27.5 → 0.28.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/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../../node_modules/.pnpm/args-tokens@0.23.0/node_modules/args-tokens/lib/parser-C6MbpZjd.d.ts
1
+ //#region ../../node_modules/.pnpm/args-tokens@0.23.1/node_modules/args-tokens/lib/parser.d.ts
2
2
  //#region src/parser.d.ts
3
3
  /**
4
4
  * Entry point of argument parser.
@@ -55,9 +55,8 @@ interface ArgToken {
55
55
  * Parser Options.
56
56
  */
57
57
  //#endregion
58
- //#region ../../node_modules/.pnpm/args-tokens@0.23.0/node_modules/args-tokens/lib/resolver-D64nGlCD.d.ts
58
+ //#region ../../node_modules/.pnpm/args-tokens@0.23.1/node_modules/args-tokens/lib/resolver.d.ts
59
59
  //#region src/resolver.d.ts
60
-
61
60
  /**
62
61
  * An argument schema definition for command-line argument parsing.
63
62
  *
@@ -504,7 +503,7 @@ type ResolveArgValues<A extends Args, V extends Record<keyof A, unknown>> = { -r
504
503
  *
505
504
  * @internal
506
505
  */
507
- type FilterArgs<A extends Args, V extends Record<keyof A, unknown>, K$1 extends keyof ArgSchema> = { [Arg in keyof A as A[Arg][K$1] extends {} ? Arg : never]: V[Arg] };
506
+ type FilterArgs<A extends Args, V extends Record<keyof A, unknown>, K extends keyof ArgSchema> = { [Arg in keyof A as A[Arg][K] extends {} ? Arg : never]: V[Arg] };
508
507
  /**
509
508
  * Filters positional arguments from the argument schema.
510
509
  *
@@ -717,7 +716,7 @@ type ExtendContext = Record<string, unknown>;
717
716
  *
718
717
  * @since v0.27.0
719
718
  */
720
- interface GunshiParams<P$1 extends {
719
+ interface GunshiParams<P extends {
721
720
  args?: Args;
722
721
  extensions?: ExtendContext;
723
722
  } = {
@@ -727,13 +726,13 @@ interface GunshiParams<P$1 extends {
727
726
  /**
728
727
  * Command argument definitions.
729
728
  */
730
- args: P$1 extends {
729
+ args: P extends {
731
730
  args: infer A extends Args;
732
731
  } ? A : Args;
733
732
  /**
734
733
  * Command context extensions.
735
734
  */
736
- extensions: P$1 extends {
735
+ extensions: P extends {
737
736
  extensions: infer E extends ExtendContext;
738
737
  } ? E : {};
739
738
  }
@@ -1069,6 +1068,15 @@ interface CommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
1069
1068
  * The command call mode is `entry` when the command is executed as an entry command, and `subCommand` when the command is executed as a sub-command.
1070
1069
  */
1071
1070
  callMode: CommandCallMode;
1071
+ /**
1072
+ * The path of nested sub-commands that were resolved to reach the current command.
1073
+ *
1074
+ * For example, if the user runs `git remote add`, `commandPath` would be `['remote', 'add']`.
1075
+ * For the entry command, this is an empty array.
1076
+ *
1077
+ * @since v0.28.0
1078
+ */
1079
+ commandPath: string[];
1072
1080
  /**
1073
1081
  * Whether to convert the camel-case style argument name to kebab-case.
1074
1082
  * This context value is set from {@linkcode Command.toKebab} option.
@@ -1210,6 +1218,15 @@ interface Command<G extends GunshiParamsConstraint = DefaultGunshiParams> {
1210
1218
  * @since v0.27.0
1211
1219
  */
1212
1220
  rendering?: RenderingOptions<G>;
1221
+ /**
1222
+ * Nested sub-commands for this command.
1223
+ *
1224
+ * Allows building command trees like `git remote add`.
1225
+ * Each key is the sub-command name, and the value is a command or lazy command.
1226
+ *
1227
+ * @since v0.28.0
1228
+ */
1229
+ subCommands?: Record<string, SubCommandable> | Map<string, SubCommandable>;
1213
1230
  }
1214
1231
  /**
1215
1232
  * Lazy command interface.
@@ -1286,6 +1303,13 @@ interface SubCommandable {
1286
1303
  * see {@link LazyCommand.commandName}
1287
1304
  */
1288
1305
  commandName?: string;
1306
+ /**
1307
+ * Nested sub-commands for this command.
1308
+ *
1309
+ * @see {@link Command.subCommands}
1310
+ * @since v0.28.0
1311
+ */
1312
+ subCommands?: Record<string, any> | Map<string, any>;
1289
1313
  /**
1290
1314
  * Index signature to allow additional properties
1291
1315
  */
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../../node_modules/.pnpm/args-tokens@0.23.0/node_modules/args-tokens/lib/parser-M-ayhS1h.js
1
+ //#region ../../node_modules/.pnpm/args-tokens@0.23.1/node_modules/args-tokens/lib/parser.js
2
2
  const HYPHEN_CHAR = "-";
3
3
  const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
4
4
  const EQUAL_CHAR = "=";
@@ -42,11 +42,11 @@ function parseArgs(args, options = {}) {
42
42
  kind: "option-terminator",
43
43
  index
44
44
  });
45
- const mapped = remainings.map((arg$1) => {
45
+ const mapped = remainings.map((arg) => {
46
46
  return {
47
47
  kind: "positional",
48
48
  index: ++index,
49
- value: arg$1
49
+ value: arg
50
50
  };
51
51
  });
52
52
  tokens.push(...mapped);
@@ -196,7 +196,7 @@ function hasOptionValue(value) {
196
196
  }
197
197
 
198
198
  //#endregion
199
- //#region ../../node_modules/.pnpm/args-tokens@0.23.0/node_modules/args-tokens/lib/utils-1LQrGCWG.js
199
+ //#region ../../node_modules/.pnpm/args-tokens@0.23.1/node_modules/args-tokens/lib/utils-CxvkckUD.js
200
200
  /**
201
201
  * Entry point of utils.
202
202
  *
@@ -219,7 +219,16 @@ function kebabnize(str) {
219
219
  }
220
220
 
221
221
  //#endregion
222
- //#region ../../node_modules/.pnpm/args-tokens@0.23.0/node_modules/args-tokens/lib/resolver-D0hj6HpX.js
222
+ //#region ../../node_modules/.pnpm/args-tokens@0.23.1/node_modules/args-tokens/lib/resolver.js
223
+ /**
224
+ * Entry point of argument options resolver.
225
+ *
226
+ * @module
227
+ */
228
+ /**
229
+ * @author kazuya kawaguchi (a.k.a. kazupon)
230
+ * @license MIT
231
+ */
223
232
  const SKIP_POSITIONAL_DEFAULT = -1;
224
233
  /**
225
234
  * Resolve command line arguments.
@@ -262,7 +271,7 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
262
271
  let currentShortOption;
263
272
  const expandableShortOptions = [];
264
273
  function toShortValue() {
265
- if (expandableShortOptions.length === 0) return void 0;
274
+ if (expandableShortOptions.length === 0) return;
266
275
  else {
267
276
  const value = expandableShortOptions.map((token) => token.name).join("");
268
277
  expandableShortOptions.length = 0;
@@ -369,11 +378,14 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
369
378
  if (remainingPositionals.length > 0) {
370
379
  values[rawArg] = remainingPositionals.map((p) => p.value);
371
380
  positionalsCount += remainingPositionals.length;
381
+ explicit[rawArg] = true;
372
382
  } else if (schema.required) errors.push(createRequireError(arg, schema));
373
383
  } else {
374
384
  const positional = positionalTokens[positionalsCount];
375
- if (positional != null) values[rawArg] = positional.value;
376
- else errors.push(createRequireError(arg, schema));
385
+ if (positional != null) {
386
+ values[rawArg] = positional.value;
387
+ explicit[rawArg] = true;
388
+ } else errors.push(createRequireError(arg, schema));
377
389
  positionalsCount++;
378
390
  }
379
391
  continue;
@@ -543,7 +555,8 @@ async function resolveLazyCommand(cmd, name, needRunResolving = false) {
543
555
  args: cmd.args,
544
556
  examples: cmd.examples,
545
557
  internal: cmd.internal,
546
- entry: cmd.entry
558
+ entry: cmd.entry,
559
+ subCommands: cmd.subCommands
547
560
  };
548
561
  if ("resource" in cmd && cmd.resource) baseCommand.resource = cmd.resource;
549
562
  command = Object.assign(create(), baseCommand);
@@ -559,6 +572,7 @@ async function resolveLazyCommand(cmd, name, needRunResolving = false) {
559
572
  command.examples = loaded.examples;
560
573
  command.internal = loaded.internal;
561
574
  command.entry = loaded.entry;
575
+ command.subCommands = loaded.subCommands || cmd.subCommands;
562
576
  if ("resource" in loaded && loaded.resource) command.resource = loaded.resource;
563
577
  } else throw new TypeError(`Cannot resolve command: ${cmd.name || name}`);
564
578
  }
@@ -584,6 +598,24 @@ function log(...args) {
584
598
  console.log(...args);
585
599
  }
586
600
  /**
601
+ * Get the sub-commands of a command as a normalized Map.
602
+ *
603
+ * @param cmd - A command or lazy command
604
+ * @returns A Map of sub-commands, or undefined if the command has no sub-commands.
605
+ */
606
+ function getCommandSubCommands(cmd) {
607
+ const subCommands = isLazyCommand(cmd) ? cmd.subCommands : typeof cmd === "object" ? cmd.subCommands : void 0;
608
+ if (!subCommands) return;
609
+ if (subCommands instanceof Map) return subCommands.size > 0 ? subCommands : void 0;
610
+ if (typeof subCommands === "object") {
611
+ const entries = Object.entries(subCommands);
612
+ if (entries.length === 0) return;
613
+ const map = /* @__PURE__ */ new Map();
614
+ for (const [name, cmd] of entries) map.set(name, cmd);
615
+ return map;
616
+ }
617
+ }
618
+ /**
587
619
  * Deep freeze an object, making it immutable.
588
620
  *
589
621
  * @param obj - The object to freeze
@@ -623,7 +655,7 @@ function deepFreeze(obj, ignores = []) {
623
655
  * @param param - A {@link CommandContextParams | parameters} to create a command context.
624
656
  * @returns A {@link CommandContext | command context}, which is readonly.
625
657
  */
626
- async function createCommandContext({ args = {}, explicit = {}, values = {}, positionals = [], rest = [], argv = [], tokens = [], command = {}, extensions = {}, cliOptions = {}, callMode = "entry", omitted = false, validationError = void 0 }) {
658
+ async function createCommandContext({ args = {}, explicit = {}, values = {}, positionals = [], rest = [], argv = [], tokens = [], command = {}, extensions = {}, cliOptions = {}, callMode = "entry", commandPath = [], omitted = false, validationError = void 0 }) {
627
659
  /**
628
660
  * normailize the options schema and values, to avoid prototype pollution
629
661
  */
@@ -652,6 +684,7 @@ async function createCommandContext({ args = {}, explicit = {}, values = {}, pos
652
684
  description: command.description,
653
685
  omitted,
654
686
  callMode,
687
+ commandPath,
655
688
  env,
656
689
  args: _args,
657
690
  explicit,
@@ -869,16 +902,18 @@ async function cliCore(argv, entry, options, plugins) {
869
902
  const resolvedPlugins = await applyPlugins(pluginContext, [...plugins, ...options.plugins || []]);
870
903
  const cliOptions = normalizeCliOptions(options, decorators, pluginContext);
871
904
  const tokens = parseArgs(argv);
872
- const subCommand = getSubCommand(tokens);
873
- const { commandName: name, command, callMode } = resolveCommand(subCommand, entry, cliOptions);
905
+ const resolved = resolveCommandTree(tokens, entry, cliOptions);
906
+ const { commandName: name, command, callMode, commandPath, depth, levelSubCommands } = resolved;
874
907
  if (!command) throw new Error(`Command not found: ${name || ""}`);
875
908
  const args = resolveArguments(pluginContext, getCommandArgs(command));
909
+ const skipPositional = depth > 0 ? depth - 1 : -1;
876
910
  const { explicit, values, positionals, rest, error } = resolveArgs(args, tokens, {
877
911
  shortGrouping: true,
878
912
  toKebab: command.toKebab,
879
- skipPositional: callMode === "subCommand" && cliOptions.subCommands.size > 0 ? 0 : -1
913
+ skipPositional
880
914
  });
881
- const omitted = !subCommand;
915
+ const omitted = resolved.omitted;
916
+ if (levelSubCommands) cliOptions.subCommands = levelSubCommands;
882
917
  const resolvedCommand = isLazyCommand(command) ? await resolveLazyCommand(command, name, true) : command;
883
918
  return await executeCommand(resolvedCommand, await createCommandContext({
884
919
  args,
@@ -890,6 +925,7 @@ async function cliCore(argv, entry, options, plugins) {
890
925
  tokens,
891
926
  omitted,
892
927
  callMode,
928
+ commandPath,
893
929
  command: resolvedCommand,
894
930
  extensions: getPluginExtensions(resolvedPlugins),
895
931
  validationError: error,
@@ -926,9 +962,12 @@ function createInitialSubCommands(options, entryCmd) {
926
962
  const subCommands = new Map(options.subCommands instanceof Map ? options.subCommands : []);
927
963
  if (!(options.subCommands instanceof Map) && isObject(options.subCommands)) for (const [name, cmd] of Object.entries(options.subCommands)) subCommands.set(name, cmd);
928
964
  if (hasSubCommands) {
929
- if (isLazyCommand(entryCmd) || typeof entryCmd === "object") {
930
- entryCmd.entry = true;
931
- subCommands.set(resolveEntryName(entryCmd), entryCmd);
965
+ if (isLazyCommand(entryCmd)) {
966
+ const entryCopy = Object.assign((...args) => entryCmd(...args), entryCmd, { entry: true });
967
+ subCommands.set(resolveEntryName(entryCopy), entryCopy);
968
+ } else if (typeof entryCmd === "object") {
969
+ const entryCopy = Object.assign(create(), entryCmd, { entry: true });
970
+ subCommands.set(resolveEntryName(entryCopy), entryCopy);
932
971
  } else if (typeof entryCmd === "function") {
933
972
  const name = entryCmd.name || ANONYMOUS_COMMAND_NAME;
934
973
  subCommands.set(name, {
@@ -948,48 +987,102 @@ function normalizeCliOptions(options, decorators, pluginContext) {
948
987
  if (resolvedOptions.renderValidationErrors === void 0) resolvedOptions.renderValidationErrors = decorators.getValidationErrorsRenderer();
949
988
  return resolvedOptions;
950
989
  }
951
- function getSubCommand(tokens) {
952
- const firstToken = tokens[0];
953
- return firstToken && firstToken.kind === "positional" && firstToken.index === 0 && firstToken.value ? firstToken.value : "";
990
+ function getPositionalTokens(tokens) {
991
+ return tokens.filter((t) => t.kind === "positional").map((t) => t.value).filter((v) => !!v);
954
992
  }
955
- const CANNOT_RESOLVE_COMMAND = { callMode: "unexpected" };
956
- function resolveCommand(sub, entry, options) {
957
- const omitted = !sub;
958
- function doResolveCommand() {
993
+ function resolveCommandTree(tokens, entry, options) {
994
+ const positionals = getPositionalTokens(tokens);
995
+ function resolveAsEntry() {
959
996
  if (typeof entry === "function") if ("commandName" in entry && entry.commandName) return {
960
997
  commandName: entry.commandName,
961
998
  command: entry,
962
- callMode: "entry"
999
+ callMode: "entry",
1000
+ commandPath: [],
1001
+ depth: 0,
1002
+ omitted: options.subCommands.size > 0 && !positionals[0],
1003
+ levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
963
1004
  };
964
1005
  else return {
965
1006
  command: {
966
1007
  run: entry,
967
1008
  entry: true
968
1009
  },
969
- callMode: "entry"
1010
+ callMode: "entry",
1011
+ commandPath: [],
1012
+ depth: 0,
1013
+ omitted: options.subCommands.size > 0 && !positionals[0],
1014
+ levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
970
1015
  };
971
1016
  else if (typeof entry === "object") return {
972
1017
  commandName: resolveEntryName(entry),
973
1018
  command: entry,
974
- callMode: "entry"
1019
+ callMode: "entry",
1020
+ commandPath: [],
1021
+ depth: 0,
1022
+ omitted: options.subCommands.size > 0 && !positionals[0],
1023
+ levelSubCommands: options.subCommands.size > 0 ? options.subCommands : void 0
975
1024
  };
976
- else return CANNOT_RESOLVE_COMMAND;
977
- }
978
- if (omitted || options.subCommands?.size === 0) return doResolveCommand();
979
- const cmd = options.subCommands?.get(sub);
980
- if (cmd == null) {
981
- if (options.fallbackToEntry) return doResolveCommand();
982
- return {
983
- commandName: sub,
984
- callMode: "unexpected"
1025
+ else return {
1026
+ callMode: "unexpected",
1027
+ commandPath: [],
1028
+ depth: 0,
1029
+ omitted: false,
1030
+ levelSubCommands: void 0
985
1031
  };
986
1032
  }
987
- if (isLazyCommand(cmd) && cmd.commandName == null) cmd.commandName = sub;
988
- else if (typeof cmd === "object" && cmd.name == null) cmd.name = sub;
1033
+ if (positionals.length === 0 || options.subCommands.size === 0) return resolveAsEntry();
1034
+ let currentSubCommands = options.subCommands;
1035
+ let resolvedCommand;
1036
+ let resolvedName;
1037
+ const commandPath = [];
1038
+ let depth = 0;
1039
+ for (let i = 0; i < positionals.length; i++) {
1040
+ const token = positionals[i];
1041
+ const cmd = currentSubCommands.get(token);
1042
+ if (cmd == null) {
1043
+ if (depth === 0) {
1044
+ if (options.fallbackToEntry) return resolveAsEntry();
1045
+ return {
1046
+ commandName: token,
1047
+ callMode: "unexpected",
1048
+ commandPath: [],
1049
+ depth: 0,
1050
+ omitted: false,
1051
+ levelSubCommands: void 0
1052
+ };
1053
+ }
1054
+ break;
1055
+ }
1056
+ let resolved = cmd;
1057
+ if (typeof cmd === "function" && cmd.commandName == null) resolved = Object.assign((...args) => cmd(...args), cmd, { commandName: token });
1058
+ else if (typeof cmd === "object" && cmd.name == null) resolved = Object.assign(create(), cmd, { name: token });
1059
+ resolvedCommand = resolved;
1060
+ resolvedName = token;
1061
+ commandPath.push(token);
1062
+ depth++;
1063
+ const nestedSubCommands = getCommandSubCommands(cmd);
1064
+ if (nestedSubCommands && nestedSubCommands.size > 0) currentSubCommands = nestedSubCommands;
1065
+ else break;
1066
+ }
1067
+ if (!resolvedCommand) return resolveAsEntry();
1068
+ const resolvedSubCommands = getCommandSubCommands(resolvedCommand);
1069
+ const omitted = resolvedSubCommands != null && resolvedSubCommands.size > 0;
1070
+ let levelSubCommands;
1071
+ if (omitted && resolvedSubCommands) {
1072
+ levelSubCommands = new Map(resolvedSubCommands);
1073
+ let entryCopy;
1074
+ if (typeof resolvedCommand === "function") entryCopy = Object.assign((...args) => resolvedCommand(...args), resolvedCommand, { entry: true });
1075
+ else entryCopy = Object.assign(create(), resolvedCommand, { entry: true });
1076
+ levelSubCommands.set(resolvedName || resolveEntryName(entryCopy), entryCopy);
1077
+ }
989
1078
  return {
990
- commandName: sub,
991
- command: cmd,
992
- callMode: "subCommand"
1079
+ commandName: resolvedName,
1080
+ command: resolvedCommand,
1081
+ callMode: depth > 0 ? "subCommand" : "entry",
1082
+ commandPath,
1083
+ depth,
1084
+ omitted,
1085
+ levelSubCommands
993
1086
  };
994
1087
  }
995
1088
  function resolveEntryName(entry) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gunshi/bone",
3
3
  "description": "gunshi minimum",
4
- "version": "0.27.5",
4
+ "version": "0.28.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -56,10 +56,10 @@
56
56
  "jsr-exports-lint": "^0.4.1",
57
57
  "publint": "^0.3.16",
58
58
  "tsdown": "0.15.12",
59
- "@gunshi/definition": "0.27.5",
60
- "@gunshi/plugin-global": "0.27.5",
61
- "@gunshi/plugin-renderer": "0.27.5",
62
- "gunshi": "0.27.5"
59
+ "@gunshi/definition": "0.28.0",
60
+ "@gunshi/plugin-global": "0.28.0",
61
+ "@gunshi/plugin-renderer": "0.28.0",
62
+ "gunshi": "0.28.0"
63
63
  },
64
64
  "scripts": {
65
65
  "build": "tsdown",