@breadc/core 1.0.0-beta.2 → 1.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,18 @@
1
- # @breadc/core
1
+ # 🥪 Breadc
2
2
 
3
- [![version](https://img.shields.io/npm/v/breadc?label=Breadc)](https://www.npmjs.com/package/breadc) [![CI](https://github.com/yjl9903/Breadc/actions/workflows/ci.yml/badge.svg)](https://github.com/yjl9903/Breadc/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/yjl9903/Breadc/branch/main/graph/badge.svg?token=F7PGOG62EF)](https://codecov.io/gh/yjl9903/Breadc)
3
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/yjl9903/Breadc)
4
+ [![version](https://img.shields.io/npm/v/breadc?label=Breadc)](https://www.npmjs.com/package/breadc)
5
+ [![CI](https://github.com/yjl9903/Breadc/actions/workflows/ci.yml/badge.svg)](https://github.com/yjl9903/Breadc/actions/workflows/ci.yml)
6
+ [![codecov](https://codecov.io/gh/yjl9903/Breadc/branch/main/graph/badge.svg?token=F7PGOG62EF)](https://codecov.io/gh/yjl9903/Breadc)
4
7
 
5
- Yet another Command Line Application Framework with fully strong **[TypeScript](https://www.typescriptlang.org/) support**.
8
+ Yet another **Command Line Application Framework** desgined for **[TypeScript](https://www.typescriptlang.org/)**.
9
+
10
+ - **TypeScript Infer**: infer command arguments, option values, and action signatures in IDE automatically
11
+ - **Command**: support default command, command alias, and nested sub-commands like `git remote add <name> <url>`
12
+ - **Group**: organize commands by modules and build large multi-command CLI applications with clear structure
13
+ - **Option**: support boolean, required, optional, spread options, `--no-*` negation, and `--` passthrough arguments
14
+ - **Middleware**: support middleware pipeline and unknown option handling
15
+ - **Builtin CLI Features**: provide common help / version options and i18n support out of the box
6
16
 
7
17
  ![vscode](https://raw.githubusercontent.com/yjl9903/Breadc/v1.0.0-beta.1/assets/typescript.png)
8
18
 
@@ -39,8 +49,9 @@ If you are using IDEs that support TypeScript (like [Visual Studio Code](https:/
39
49
 
40
50
  ## Inspiration
41
51
 
42
- + [cac](https://github.com/cacjs/cac): Simple yet powerful framework for building command-line apps.
43
- + [TypeScript: Documentation - Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html)
52
+ - [cac](https://github.com/cacjs/cac): Simple yet powerful framework for building command-line apps.
53
+ - [Commander.js](https://github.com/tj/commander.js): Node.js command-line interfaces made easy.
54
+ - [TypeScript: Documentation - Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html)
44
55
 
45
56
  ## License
46
57
 
package/dist/index.d.mts CHANGED
@@ -321,7 +321,7 @@ type InternalCommand = Command & {
321
321
  /**
322
322
  * @internal
323
323
  */
324
- _actionFn?: Function;
324
+ _actionFn?: (...args: any[]) => unknown;
325
325
  };
326
326
  type OptionType = 'boolean' | 'required' | 'optional' | 'spread';
327
327
  type InternalOption = Option & {
@@ -529,9 +529,9 @@ type Breadc<Data extends {} = {}, Options extends Record<never, never> = {}> = {
529
529
  */
530
530
  use<Middleware extends ActionMiddleware<Data, ActionMiddlewareNextFn>>(middleware: Middleware): Breadc<InferMiddlewareData<Middleware>, Options>;
531
531
  /**
532
- * Allow unknown option middleware
532
+ * Unknown command middleware
533
533
  */
534
- onUnknownCommand(middleware?: boolean | UnknownCommandMiddleware<Data>): Breadc<Data, Options>;
534
+ onUnknownCommand(middleware?: UnknownCommandMiddleware<Data>): Breadc<Data, Options>;
535
535
  /**
536
536
  * Allow unknown option middleware
537
537
  */
@@ -567,7 +567,7 @@ type Group<Spec extends string = string, Init extends GroupInit<Spec> = GroupIni
567
567
  option<OS extends string, Initial extends NonTrueNullable<InferOptionInitialType<OS>>, OI extends NonNullableOptionInit<OS, Initial>>(spec: OS, description: string, init: OI): Group<Spec, Init, Data, Options & InferOption<OS, Initial, OI>>;
568
568
  option<OS extends string, Initial extends InferOptionInitialType<OS>, OI extends OptionInit<OS, Initial>>(spec: OS, description?: string, init?: OI): Group<Spec, Init, Data, Options & InferOption<OS, Initial, OI>>;
569
569
  command<S extends string, I extends CommandInit<S>>(spec: S, description?: string, init?: I): Command<S, I, Data, Options, InferArgumentsType<S>, unknown>;
570
- command<S extends string, I extends CommandInit<S>>(command: Command<S, I, Options>): Command<S, I, Data, Options, InferArgumentsType<S>, unknown>;
570
+ command<S extends string, I extends CommandInit<S>>(command: Command<S, I, Data, Options>): Command<S, I, Data, Options, InferArgumentsType<S>, unknown>;
571
571
  /**
572
572
  * Action middleware
573
573
  */
@@ -577,7 +577,7 @@ type Group<Spec extends string = string, Init extends GroupInit<Spec> = GroupIni
577
577
  */
578
578
  allowUnknownOption(middleware?: UnknownOptionMiddleware<Data>): Group<Spec, Init, Data, Options>;
579
579
  };
580
- type Command<Spec extends string = string, Init extends CommandInit<Spec> = CommandInit<Spec>, Data extends {} = {}, Options extends Record<never, never> = {}, Arguments extends unknown[] = InferArgumentsType<Spec>, Return extends unknown = unknown> = {
580
+ type Command<Spec extends string = string, Init extends CommandInit<Spec> = CommandInit<Spec>, Data extends {} = {}, Options extends Record<never, never> = Record<never, never>, Arguments extends unknown[] = InferArgumentsType<Spec>, Return extends unknown = unknown> = {
581
581
  spec: Spec;
582
582
  init: Init | undefined;
583
583
  /**
@@ -657,6 +657,7 @@ declare function printVersion(context: Context): string;
657
657
  //#region src/error.d.ts
658
658
  declare abstract class BreadcError extends Error {}
659
659
  type BreadcAppErrorCause = {
660
+ group?: InternalGroup;
660
661
  command?: InternalCommand;
661
662
  commands?: (InternalCommand | InternalGroup)[];
662
663
  };
@@ -665,6 +666,7 @@ type BreadcAppErrorInput = BreadcAppErrorCause & {
665
666
  };
666
667
  declare class BreadcAppError extends BreadcError {
667
668
  static DUPLICATED_DEFAULT_COMMAND: string;
669
+ static DUPLICATED_DEFAULT_GROUP_COMMAND: string;
668
670
  static DUPLICATED_GROUP: string;
669
671
  static DUPLICATED_COMMAND: string;
670
672
  static NO_ACTION_BOUND: string;
package/dist/index.mjs CHANGED
@@ -38,6 +38,7 @@ function rawOption(spec, type, long, short, init) {
38
38
  //#region src/error.ts
39
39
  var BreadcError = class extends Error {};
40
40
  var RuntimeError = class extends BreadcError {
41
+ static UNEXPECTED_ARGUMENTS = "Detect unexpected redundant arguments";
41
42
  static REQUIRED_ARGUMENT_MISSING = "Missing required argument";
42
43
  static OPTIONAL_ARGUMENT_ACCEPT_ONCE = "Optional argument can only be assigned once";
43
44
  static REQUIRED_ARGUMENT_ACCEPT_ONCE = "Required argument can only be assigned once";
@@ -60,6 +61,7 @@ var RuntimeError = class extends BreadcError {
60
61
  };
61
62
  var BreadcAppError = class extends BreadcError {
62
63
  static DUPLICATED_DEFAULT_COMMAND = `Find duplicated default commands`;
64
+ static DUPLICATED_DEFAULT_GROUP_COMMAND = `Find duplicated default group commands`;
63
65
  static DUPLICATED_GROUP = `Find duplicated groups`;
64
66
  static DUPLICATED_COMMAND = `Find duplicated commands`;
65
67
  static NO_ACTION_BOUND = `There is no action function bound in this command`;
@@ -112,6 +114,16 @@ var ResolveOptionError = class extends BreadcError {
112
114
  }
113
115
  };
114
116
 
117
+ //#endregion
118
+ //#region src/breadc/shared.ts
119
+ function resolveOptionInput(spec, description, init) {
120
+ return typeof spec === "string" ? option(spec, description, init) : spec;
121
+ }
122
+ const defaultUnknownOptionMiddleware = (_ctx, key, value) => ({
123
+ name: key,
124
+ value
125
+ });
126
+
115
127
  //#endregion
116
128
  //#region src/breadc/command.ts
117
129
  function command(spec, init) {
@@ -134,8 +146,7 @@ function command(spec, init) {
134
146
  return run;
135
147
  };
136
148
  run.option = (spec, description, init) => {
137
- const option$5 = typeof spec === "string" ? option(spec, description, init) : spec;
138
- options.push(option$5);
149
+ options.push(resolveOptionInput(spec, description, init));
139
150
  return run;
140
151
  };
141
152
  run.use = (middleware) => {
@@ -146,10 +157,7 @@ function command(spec, init) {
146
157
  run.allowUnknownOption = (middleware) => {
147
158
  if (!run._unknownOptionMiddlewares) run._unknownOptionMiddlewares = [];
148
159
  if (typeof middleware === "function") run._unknownOptionMiddlewares.push(middleware);
149
- else run._unknownOptionMiddlewares.push((_ctx, key, value) => ({
150
- name: key,
151
- value
152
- }));
160
+ else run._unknownOptionMiddlewares.push(defaultUnknownOptionMiddleware);
153
161
  return run;
154
162
  };
155
163
  run.action = (fn) => {
@@ -799,9 +807,9 @@ function buildVersionOption(context) {
799
807
  const { breadc } = context;
800
808
  if (breadc._version) return breadc._version;
801
809
  const spec = typeof breadc._init.builtin?.version === "object" ? breadc._init.builtin.version.spec : void 0;
802
- const option$4 = spec ? resolveOption(option(spec, "Print version")) : rawOption("-v, --version", "boolean", "version", "v", { description: "Print version" });
803
- breadc._version = option$4;
804
- return option$4;
810
+ const option$2 = spec ? resolveOption(option(spec, "Print version")) : rawOption("-v, --version", "boolean", "version", "v", { description: "Print version" });
811
+ breadc._version = option$2;
812
+ return option$2;
805
813
  }
806
814
  function printVersion(context) {
807
815
  const { breadc } = context;
@@ -849,7 +857,7 @@ function formatCommand(command) {
849
857
  const args = formatArgument(command).join(" ");
850
858
  return pieces && args ? `${pieces} ${args}` : pieces;
851
859
  }
852
- function commandMatched(command, prefix) {
860
+ function commandStartsWith(command, prefix) {
853
861
  if (prefix.length === 0) return true;
854
862
  for (const pieces of command._pieces) {
855
863
  if (pieces.length < prefix.length) continue;
@@ -862,6 +870,18 @@ function commandMatched(command, prefix) {
862
870
  }
863
871
  return false;
864
872
  }
873
+ function commandIncludes(command, prefix) {
874
+ for (const pieces of command._pieces) {
875
+ if (pieces.length > prefix.length) continue;
876
+ let ok = true;
877
+ for (let i = 0; i < pieces.length; i++) if (pieces[i] !== prefix[i]) {
878
+ ok = false;
879
+ break;
880
+ }
881
+ if (ok) return true;
882
+ }
883
+ return false;
884
+ }
865
885
  function collect(context, pieces) {
866
886
  const allCommands = [];
867
887
  const commands = [];
@@ -876,17 +896,17 @@ function collect(context, pieces) {
876
896
  resolveGroup(group);
877
897
  buildGroup(group);
878
898
  allCommands.push(...group._commands);
879
- if (commandMatched(group, pieces)) for (const option of group._options) append(option);
880
- for (const command of group._commands) if (commandMatched(command, pieces)) {
899
+ if (commandIncludes(group, pieces)) for (const option of group._options) append(option);
900
+ for (const command of group._commands) if (commandStartsWith(command, pieces)) {
881
901
  commands.push(command);
882
- for (const option of command._options) append(option);
902
+ if (commandIncludes(command, pieces)) for (const option of command._options) append(option);
883
903
  }
884
904
  } else {
885
905
  resolveCommand(command);
886
906
  allCommands.push(command);
887
- if (commandMatched(command, pieces)) {
907
+ if (commandStartsWith(command, pieces)) {
888
908
  commands.push(command);
889
- for (const option of command._options) append(option);
909
+ if (commandIncludes(command, pieces)) for (const option of command._options) append(option);
890
910
  }
891
911
  }
892
912
  if (context.breadc._init.builtin?.help !== false) append(buildHelpOption(context));
@@ -901,9 +921,9 @@ function buildHelpOption(context) {
901
921
  const { breadc } = context;
902
922
  if (breadc._help) return breadc._help;
903
923
  const spec = typeof breadc._init.builtin?.help === "object" ? breadc._init.builtin.help.spec : void 0;
904
- const option$3 = spec ? resolveOption(option(spec, "Print help")) : rawOption("-h, --help", "boolean", "help", "h", { description: "Print help" });
905
- breadc._help = option$3;
906
- return option$3;
924
+ const option$1 = spec ? resolveOption(option(spec, "Print help")) : rawOption("-h, --help", "boolean", "help", "h", { description: "Print help" });
925
+ breadc._help = option$1;
926
+ return option$1;
907
927
  }
908
928
  function printHelp(context) {
909
929
  const { breadc, pieces } = context;
@@ -950,10 +970,14 @@ function parse(app, argv) {
950
970
  commands: defaultCommands
951
971
  });
952
972
  const defaultCommand = defaultCommands[0];
953
- doParse(context$1, defaultCommand !== void 0 && context$1.breadc._commands.length === 1 ? defaultCommand : void 0);
954
- if (context$1.command || isVersion(context$1) || isHelp(context$1)) {} else if (defaultCommand) {
973
+ doParse(context$1, void 0, defaultCommand !== void 0 && context$1.breadc._commands.length === 1 ? defaultCommand : void 0);
974
+ if (context$1.command || isVersion(context$1) || isHelp(context$1)) {} else if (context$1.group) {
975
+ const matchedGroup = context$1.group;
976
+ reset(context$1);
977
+ doParse(context$1, matchedGroup, void 0);
978
+ } else if (defaultCommand) {
955
979
  reset(context$1);
956
- doParse(context$1, defaultCommand);
980
+ doParse(context$1, void 0, defaultCommand);
957
981
  }
958
982
  return context$1;
959
983
  }
@@ -973,7 +997,7 @@ function isVersion(context) {
973
997
  if (version && context.options.get(version.long)?.value()) return true;
974
998
  else return false;
975
999
  }
976
- function doParse(context, defaultCommand) {
1000
+ function doParse(context, defaultGroup, defaultCommand) {
977
1001
  const { breadc, tokens, options: matchedOptions } = context;
978
1002
  let index = 0;
979
1003
  let matchedGroup = void 0;
@@ -1010,10 +1034,8 @@ function doParse(context, defaultCommand) {
1010
1034
  }
1011
1035
  if (defaultCommand) {
1012
1036
  matchedCommand = defaultCommand;
1013
- for (const command of breadc._commands) if (command._default) {
1014
- buildCommand(command);
1015
- addPendingOptions(command._options);
1016
- }
1037
+ buildCommand(defaultCommand);
1038
+ addPendingOptions(defaultCommand._options);
1017
1039
  } else for (const command of breadc._commands) if (!command._default) for (let alias = 0; alias < command._pieces.length; alias++) addPendingCommand(command, alias);
1018
1040
  while (!tokens.isEnd) {
1019
1041
  const token = tokens.next();
@@ -1035,7 +1057,22 @@ function doParse(context, defaultCommand) {
1035
1057
  });
1036
1058
  buildGroup(group);
1037
1059
  addPendingOptions(group._options);
1038
- for (const command of group._commands) for (let alias = 0; alias < command._pieces.length; alias++) addPendingCommand(command, alias);
1060
+ if (matchedGroup && matchedGroup === defaultGroup) {
1061
+ const defaultCommands = matchedGroup._commands.filter((command) => command._pieces.some((p) => p.length === index));
1062
+ if (defaultCommands.length === 1) {
1063
+ const defaultCommand = defaultCommands[0];
1064
+ matchedCommand = defaultCommand;
1065
+ buildCommand(defaultCommand);
1066
+ addPendingOptions(defaultCommand._options);
1067
+ } else if (defaultCommands.length > 1) throw new BreadcAppError(BreadcAppError.DUPLICATED_DEFAULT_GROUP_COMMAND, {
1068
+ context,
1069
+ group: matchedGroup,
1070
+ commands: defaultCommands
1071
+ });
1072
+ } else for (const command of group._commands) for (let alias = 0; alias < command._pieces.length; alias++) {
1073
+ if (command._pieces[alias].length === index) continue;
1074
+ addPendingCommand(command, alias);
1075
+ }
1039
1076
  } else {
1040
1077
  if (!matchedCommand || matchedCommand === command) matchedCommand = command;
1041
1078
  else throw new BreadcAppError(BreadcAppError.DUPLICATED_COMMAND, {
@@ -1075,6 +1112,7 @@ function doParse(context, defaultCommand) {
1075
1112
  context.group = matchedGroup;
1076
1113
  context.command = matchedCommand;
1077
1114
  if (matchedCommand) {
1115
+ if (unknown.length > 0) throw new RuntimeError(RuntimeError.UNEXPECTED_ARGUMENTS, { context });
1078
1116
  let i = 0;
1079
1117
  for (; i < matchedCommand._arguments.length; i++) {
1080
1118
  const argument = matchedCommand._arguments[i];
@@ -1169,12 +1207,14 @@ function group(spec, init) {
1169
1207
  _actionMiddlewares: actionMiddlewares,
1170
1208
  _unknownOptionMiddlewares: unknownOptionMiddlewares,
1171
1209
  option(spec, description, init) {
1172
- const option$2 = typeof spec === "string" ? option(spec, description, init) : spec;
1173
- options.push(option$2);
1210
+ options.push(resolveOptionInput(spec, description, init));
1174
1211
  return group;
1175
1212
  },
1176
- command(spec, init) {
1177
- const command$2 = typeof spec === "string" ? command(spec, init) : spec;
1213
+ command(spec, description, init) {
1214
+ const command$2 = typeof spec === "string" ? command(spec, description || init ? {
1215
+ description,
1216
+ ...init
1217
+ } : void 0) : spec;
1178
1218
  command$2._group = group;
1179
1219
  commands.push(command$2);
1180
1220
  return command$2;
@@ -1185,10 +1225,7 @@ function group(spec, init) {
1185
1225
  },
1186
1226
  allowUnknownOption(middleware) {
1187
1227
  if (typeof middleware === "function") unknownOptionMiddlewares.push(middleware);
1188
- else unknownOptionMiddlewares.push((_ctx, key, value) => ({
1189
- name: key,
1190
- value
1191
- }));
1228
+ else unknownOptionMiddlewares.push(defaultUnknownOptionMiddleware);
1192
1229
  return group;
1193
1230
  }
1194
1231
  };
@@ -1213,8 +1250,7 @@ function breadc(name, init = {}) {
1213
1250
  _unknownOptionMiddlewares: unknownOptionMiddlewares,
1214
1251
  _unknownCommandMiddlewares: unknownCommandMiddlewares,
1215
1252
  option(spec, description, init) {
1216
- const option$1 = typeof spec === "string" ? option(spec, description, init) : spec;
1217
- options.push(option$1);
1253
+ options.push(resolveOptionInput(spec, description, init));
1218
1254
  return app;
1219
1255
  },
1220
1256
  group(spec, init) {
@@ -1240,10 +1276,7 @@ function breadc(name, init = {}) {
1240
1276
  },
1241
1277
  allowUnknownOption(middleware) {
1242
1278
  if (typeof middleware === "function") unknownOptionMiddlewares.push(middleware);
1243
- else unknownOptionMiddlewares.push((_ctx, key, value) => ({
1244
- name: key,
1245
- value
1246
- }));
1279
+ else unknownOptionMiddlewares.push(defaultUnknownOptionMiddleware);
1247
1280
  return app;
1248
1281
  },
1249
1282
  parse(argv) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breadc/core",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.4",
4
4
  "description": "Yet another Command Line Application Framework with fully strong TypeScript support",
5
5
  "keywords": [
6
6
  "breadc",
@@ -33,7 +33,7 @@
33
33
  "dist"
34
34
  ],
35
35
  "dependencies": {
36
- "@breadc/color": "1.0.0-beta.2"
36
+ "@breadc/color": "1.0.0-beta.4"
37
37
  },
38
38
  "size-limit": [
39
39
  {