@reliverse/rempts 1.7.37 → 1.7.39

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
@@ -837,6 +837,37 @@ export default defineCommand({
837
837
  });
838
838
  ```
839
839
 
840
+ ### Using `runCmd` with Flexible Argument Handling
841
+
842
+ The `runCmd` function supports flexible argument passing, automatically normalizing template literals and space-separated strings:
843
+
844
+ ```ts
845
+ import { runCmd } from "@reliverse/rempts";
846
+
847
+ // Traditional way - each argument as separate array element
848
+ await runCmd(cmd, ["--dev", "true", "--name", "John"]);
849
+
850
+ // Template literals work automatically
851
+ await runCmd(cmd, [`--dev ${isDev}`]); // Automatically converted to ["--dev", "true"]
852
+ await runCmd(cmd, [`--dev ${isDev} --build mod.ts`]); // ["--dev", "true", "--build", "mod.ts"]
853
+
854
+ // Mixed arrays with template literals and regular strings
855
+ await runCmd(cmd, [
856
+ `--dev ${isDev} --build mod.ts`,
857
+ "--pub true",
858
+ "--someBoolean",
859
+ ]);
860
+
861
+ // Multiple template literals
862
+ await runCmd(cmd, [`--dev ${isDev}`, `--name ${userName}`, `--count ${count}`]);
863
+ ```
864
+
865
+ **Remember**:
866
+
867
+ - If you need to pass a value with spaces (e.g. a name like "John Doe"), you should quote it in your template literal: `await runCmd(cmd, ['--name "John Doe"']);`
868
+ - Otherwise, it will be split into two arguments: `"John"` and `"Doe"`.
869
+ - We do not handle this intentionally, because some library users might rely on this Node.js behavior and handle it themselves in their own way (e.g. space can serve as a separator for values).
870
+
840
871
  ### Loading Commands with `loadCommand`
841
872
 
842
873
  The `loadCommand` utility helps you load command files from your filesystem. It automatically handles:
@@ -926,6 +957,37 @@ export default defineCommand({
926
957
  });
927
958
  ```
928
959
 
960
+ ### Using `runCmdWithSubcommands` for Subcommands and Nested Subcommands
961
+
962
+ If you need to programmatically run commands that support subcommands (including nested subcommands), use `runCmdWithSubcommands`:
963
+
964
+ ```ts
965
+ import { runCmdWithSubcommands } from "@reliverse/rempts";
966
+
967
+ // Single-level subcommand
968
+ await runCmdWithSubcommands(mainCmd, [`build --input src/mod.ts --someBoolean`]);
969
+
970
+ // Subcommand with positional arguments
971
+ await runCmdWithSubcommands(mainCmd, [`build src/mod.ts --someBoolean`]);
972
+
973
+ // Nested subcommands
974
+ await runCmdWithSubcommands(mainCmd, [`build someSubCmd src/mod.ts --no-cjs`]);
975
+ await runCmdWithSubcommands(mainCmd, [`build sub1 sub2 sub3 file.ts --flag`]);
976
+
977
+ // Mixed array with subcommands
978
+ await runCmdWithSubcommands(mainCmd, [
979
+ `build someSubCmd src/mod.ts`,
980
+ "--no-cjs",
981
+ "--verbose"
982
+ ]);
983
+ ```
984
+
985
+ **Note:**
986
+
987
+ - `runCmdWithSubcommands` automatically normalizes template literals and space-separated strings, just like `runCmd`.
988
+ - If you need to pass a value with spaces (e.g. a name like "John Doe"), you should quote it in your template literal: `await runCmdWithSubcommands(cmd, ['--name "John Doe"']);`
989
+ - For subcommands, always use `runCmdWithSubcommands` for the most robust behavior.
990
+
929
991
  ## Argument Types: Usage Comparison
930
992
 
931
993
  Below is a demonstration of how to define and use all supported argument types in rempts: positional, boolean, string, number, and array. This includes example CLI invocations and the resulting parsed output.
@@ -1179,6 +1241,7 @@ All APIs are fully typed. See [`src/types.ts`](./src/types.ts) for advanced cust
1179
1241
  ## TODO
1180
1242
 
1181
1243
  - [ ] migrate to `dler libs` in the future (all main components will be published as separate packages; `@reliverse/rempts` will be a wrapper for all of them)
1244
+ - [ ] migrate all tests to `bun:test`
1182
1245
 
1183
1246
  ## Related
1184
1247
 
@@ -102,13 +102,61 @@ export declare function createCli<A extends ArgDefinitions = EmptyArgs>(options:
102
102
  * for IntelliSense and validation for array defaults against options.
103
103
  */
104
104
  export declare function defineArgs<A extends ArgDefinitions>(args: A): A;
105
+ /**
106
+ * Programmatically run a command with subcommand support and flexible argument handling.
107
+ * This function can handle subcommands (including nested), positional arguments, and automatically normalizes
108
+ * template literals and space-separated strings.
109
+ *
110
+ * @param command The command definition (from defineCommand)
111
+ * @param argv The argv array to parse (default: []). Supports template literals and subcommands.
112
+ * @param parserOptions Optional reliArgParser options
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * // ✅ Single command with template literals
117
+ * await runCmdWithSubcommands(cmd, [`--dev ${isDev}`]);
118
+ *
119
+ * // ✅ Subcommand with arguments
120
+ * await runCmdWithSubcommands(cmd, [`build --input src/mod.ts --someBoolean`]);
121
+ *
122
+ * // ✅ Subcommand with positional arguments
123
+ * await runCmdWithSubcommands(cmd, [`build src/mod.ts --someBoolean`]);
124
+ *
125
+ * // ✅ Nested subcommands
126
+ * await runCmdWithSubcommands(cmd, [`build someSubCmd src/mod.ts --no-cjs`]);
127
+ * await runCmdWithSubcommands(cmd, [`build sub1 sub2 sub3 file.ts --flag`]);
128
+ *
129
+ * // ✅ Mixed array with subcommands
130
+ * await runCmdWithSubcommands(cmd, [
131
+ * `build someSubCmd src/mod.ts`,
132
+ * "--no-cjs",
133
+ * "--verbose"
134
+ * ]);
135
+ * ```
136
+ */
137
+ export declare function runCmdWithSubcommands<A extends ArgDefinitions = EmptyArgs>(command: Command<A>, argv?: string[], parserOptions?: ReliArgParserOptions & {
138
+ fileBased?: FileBasedOptions;
139
+ autoExit?: boolean;
140
+ }): Promise<any>;
105
141
  /**
106
142
  * Programmatically run a command's run() handler with parsed arguments.
107
143
  * Does not handle subcommands, file-based commands, or global hooks.
108
144
  * Suitable for use in demos, tests, or programmatic invocation.
109
145
  *
110
146
  * @param command The command definition (from defineCommand)
111
- * @param argv The argv array to parse (default: [])
147
+ * @param argv The argv array to parse (default: []). Each argument should be a separate array element.
112
148
  * @param parserOptions Optional reliArgParser options
149
+ *
150
+ * @example
151
+ * **each argument as separate array element**:
152
+ * ```ts
153
+ * await runCmd(cmd, ["--dev", "true"]);
154
+ * await runCmd(cmd, ["--name", "John", "--verbose"]);
155
+ * ```
156
+ * **automatic normalization of template literals**:
157
+ * ```ts
158
+ * await runCmd(cmd, [`--dev ${isDev}`]); // Automatically converted to ["--dev", "true"]
159
+ * await runCmd(cmd, [`--dev ${isDev} --build mod.ts`]); // ["--dev", "true", "--build", "mod.ts"]
160
+ * ```
113
161
  */
114
162
  export declare function runCmd<A extends ArgDefinitions = EmptyArgs>(command: Command<A>, argv?: string[], parserOptions?: ReliArgParserOptions): Promise<void>;
@@ -1002,7 +1002,49 @@ function renderPositional(args) {
1002
1002
  export function defineArgs(args) {
1003
1003
  return args;
1004
1004
  }
1005
+ function normalizeArgv(argv) {
1006
+ const normalized = [];
1007
+ for (const arg of argv) {
1008
+ const parts = arg.split(/\s+/).filter((part) => part.length > 0);
1009
+ normalized.push(...parts);
1010
+ }
1011
+ return normalized;
1012
+ }
1013
+ export async function runCmdWithSubcommands(command, argv = [], parserOptions = {}) {
1014
+ const normalizedArgv = normalizeArgv(argv);
1015
+ let currentCommand = command;
1016
+ let currentArgv = normalizedArgv;
1017
+ while (currentCommand.commands && currentArgv.length > 0 && currentArgv[0] && !isFlag(currentArgv[0])) {
1018
+ const [maybeSub, ...restArgv] = currentArgv;
1019
+ let subSpec;
1020
+ for (const [key, spec] of Object.entries(currentCommand.commands)) {
1021
+ if (key === maybeSub) {
1022
+ subSpec = spec;
1023
+ break;
1024
+ }
1025
+ try {
1026
+ const cmd = await loadSubCommand(spec);
1027
+ if (cmd.meta?.aliases?.includes(maybeSub)) {
1028
+ subSpec = spec;
1029
+ break;
1030
+ }
1031
+ } catch (err) {
1032
+ debugLog(`Error checking alias for command ${key}:`, err);
1033
+ }
1034
+ }
1035
+ if (!subSpec) break;
1036
+ const loaded = await loadSubCommand(subSpec);
1037
+ currentCommand = loaded;
1038
+ currentArgv = restArgv;
1039
+ }
1040
+ return await runCommandWithArgs(currentCommand, currentArgv, {
1041
+ ...parserOptions,
1042
+ autoExit: false
1043
+ // Don't exit process in programmatic usage
1044
+ });
1045
+ }
1005
1046
  export async function runCmd(command, argv = [], parserOptions = {}) {
1047
+ const normalizedArgv = normalizeArgv(argv);
1006
1048
  const booleanKeys = Object.keys(command.args || {}).filter(
1007
1049
  (k) => command.args?.[k]?.type === "boolean"
1008
1050
  );
@@ -1019,7 +1061,7 @@ export async function runCmd(command, argv = [], parserOptions = {}) {
1019
1061
  boolean: [...parserOptions.boolean || [], ...booleanKeys],
1020
1062
  defaults: { ...defaultMap, ...parserOptions.defaults || {} }
1021
1063
  };
1022
- const parsed = reliArgParser(argv, mergedParserOptions);
1064
+ const parsed = reliArgParser(normalizedArgv, mergedParserOptions);
1023
1065
  debugLog("Parsed arguments (runCmd):", parsed);
1024
1066
  const finalArgs = {};
1025
1067
  const positionalKeys = Object.keys(command.args || {}).filter(
@@ -1073,6 +1115,7 @@ export async function runCmd(command, argv = [], parserOptions = {}) {
1073
1115
  }
1074
1116
  }
1075
1117
  function getParsedContext(command, argv, parserOptions) {
1118
+ const normalizedArgv = normalizeArgv(argv);
1076
1119
  const booleanKeys = Object.keys(command.args || {}).filter(
1077
1120
  (k) => command.args?.[k]?.type === "boolean"
1078
1121
  );
@@ -1089,7 +1132,7 @@ function getParsedContext(command, argv, parserOptions) {
1089
1132
  boolean: [...parserOptions.boolean || [], ...booleanKeys],
1090
1133
  defaults: { ...defaultMap, ...parserOptions.defaults || {} }
1091
1134
  };
1092
- const parsed = reliArgParser(argv, mergedParserOptions);
1135
+ const parsed = reliArgParser(normalizedArgv, mergedParserOptions);
1093
1136
  const finalArgs = {};
1094
1137
  const positionalKeys = Object.keys(command.args || {}).filter(
1095
1138
  (k) => command.args?.[k]?.type === "positional"
@@ -1,2 +1,18 @@
1
1
  import type { Command } from "./launcher-types.js";
2
+ /**
3
+ * Load a command from the filesystem.
4
+ *
5
+ * @param cmdPath - Path to the command file or directory containing cmd.ts/cmd.js
6
+ * @returns Promise<Command> - The loaded command
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * // Load a command
11
+ * const cmd = await loadCommand("./web/cmd");
12
+ *
13
+ * // Use with runCmd - pass args as separate array elements
14
+ * await runCmd(cmd, ["--dev", "true"]); // ✅ Correct
15
+ * await runCmd(cmd, [`--dev ${isDev}`]); // ❌ Wrong - creates single string
16
+ * ```
17
+ */
2
18
  export declare function loadCommand(cmdPath: string): Promise<Command>;
package/bin/mod.d.ts CHANGED
@@ -14,7 +14,7 @@ export { inputPrompt } from "./libs/input/input-mod.js";
14
14
  export { startPrompt, intro } from "./libs/intro/intro-alias.js";
15
15
  export { introPrompt } from "./libs/intro/intro-mod.js";
16
16
  export { runMain } from "./libs/launcher/launcher-alias.js";
17
- export { defineCommand, showUsage, createCli, defineArgs, runCmd, } from "./libs/launcher/launcher-mod.js";
17
+ export { defineCommand, showUsage, createCli, defineArgs, runCmd, runCmdWithSubcommands, } from "./libs/launcher/launcher-mod.js";
18
18
  export type { EmptyArgs, BaseArgProps, BaseArgDefinition, PositionalArgDefinition, BooleanArgDefinition, StringArgDefinition, NumberArgDefinition, ArrayArgDefinition, ArgDefinition, ArgDefinitions, CommandMeta, CommandSpec, CommandsMap, CommandContext, CommandRun, CommandHook, DefineCommandOptions, Command, InferArgTypes, FileBasedOptions, } from "./libs/launcher/launcher-types.js";
19
19
  export { loadCommand } from "./libs/launcher/run-command.js";
20
20
  export { addCompletions } from "./libs/launcher/trpc-orpc-support/completions.js";
package/bin/mod.js CHANGED
@@ -30,7 +30,8 @@ export {
30
30
  showUsage,
31
31
  createCli,
32
32
  defineArgs,
33
- runCmd
33
+ runCmd,
34
+ runCmdWithSubcommands
34
35
  } from "./libs/launcher/launcher-mod.js";
35
36
  export { loadCommand } from "./libs/launcher/run-command.js";
36
37
  export { addCompletions } from "./libs/launcher/trpc-orpc-support/completions.js";
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "@reliverse/reliarg": "^1.0.3",
9
9
  "@reliverse/relico": "^1.1.2",
10
10
  "@reliverse/relifso": "^1.4.5",
11
- "@reliverse/relinka": "^1.5.0",
11
+ "@reliverse/relinka": "^1.5.3",
12
12
  "@reliverse/runtime": "^1.0.3",
13
13
  "@trpc/server": "^11.4.2",
14
14
  "ansi-escapes": "^7.0.0",
@@ -44,7 +44,7 @@
44
44
  "license": "MIT",
45
45
  "name": "@reliverse/rempts",
46
46
  "type": "module",
47
- "version": "1.7.37",
47
+ "version": "1.7.39",
48
48
  "author": "reliverse",
49
49
  "bugs": {
50
50
  "email": "blefnk@gmail.com",