just-bash-util 0.1.3 → 0.1.5
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 +56 -3
- package/dist/{chunk-QKL3AOEA.js → chunk-RNQNKFXA.js} +20 -24
- package/dist/command/index.d.ts +20 -18
- package/dist/command/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,9 +28,9 @@ const cli = command("mycli", {
|
|
|
28
28
|
const serve = cli.command("serve", {
|
|
29
29
|
description: "Start the dev server",
|
|
30
30
|
options: {
|
|
31
|
-
port: o.number().default(3000).
|
|
31
|
+
port: o.number().default(3000).alias("p").describe("Port to listen on"),
|
|
32
32
|
host: o.string().describe("Host to bind to"),
|
|
33
|
-
open: f().
|
|
33
|
+
open: f().alias("o").describe("Open browser"),
|
|
34
34
|
},
|
|
35
35
|
args: [a.string().name("entry").describe("Entry file")],
|
|
36
36
|
handler: (args, ctx) => {
|
|
@@ -64,11 +64,55 @@ await serve.invoke({ port: 8080, entry: "app.ts" }, ctx);
|
|
|
64
64
|
- `omitInherited` to exclude parent options from specific subcommands
|
|
65
65
|
- `--help` / `-h` auto-generated at every level
|
|
66
66
|
- `--no-<flag>` negation, `-abc` combined short flags, `--key=value` syntax
|
|
67
|
-
- `--`
|
|
67
|
+
- `--` end-of-options separator (remaining tokens become positional args and are available via `meta.passthrough`)
|
|
68
68
|
- Environment variable fallbacks for options
|
|
69
69
|
- Levenshtein-based "did you mean?" suggestions for typos
|
|
70
70
|
- Automatic error handling — thrown errors in handlers are caught and returned as clean `ExecResult` with `exitCode: 1`
|
|
71
71
|
|
|
72
|
+
#### Options and flags
|
|
73
|
+
|
|
74
|
+
Option keys are written in camelCase and automatically converted to kebab-case for the CLI:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
options: {
|
|
78
|
+
allowEmpty: f(), // CLI: --allow-empty handler: args.allowEmpty
|
|
79
|
+
dryRun: f().alias("n"), // CLI: --dry-run / -n handler: args.dryRun
|
|
80
|
+
message: o.string().alias("m"), // CLI: --message / -m handler: args.message
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Short flags (single-dash, single-character) require `.alias()`. A single-character key like `b: f()` creates the long flag `--b`, **not** the short flag `-b`. To get `-b`, use a descriptive key with an alias:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// ✗ b: f() → creates --b (long flag), not -b
|
|
88
|
+
// ✓ branch: f().alias("b") → creates --branch and -b
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### Positional args
|
|
92
|
+
|
|
93
|
+
Args are required by default. Use `.optional()` for optional args, and `.variadic()` to collect remaining positionals into an array. Chain `.optional().variadic()` for zero-or-more:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
args: [
|
|
97
|
+
a.string().name("entry"), // required single arg
|
|
98
|
+
a.string().name("file").optional(), // optional single arg
|
|
99
|
+
a.string().name("files").variadic(), // required: one or more
|
|
100
|
+
a.string().name("paths").optional().variadic(), // optional: zero or more → string[]
|
|
101
|
+
]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### The `--` separator
|
|
105
|
+
|
|
106
|
+
The `--` token signals end-of-options. Tokens after `--` are treated as positional arguments (not parsed as flags) and are also available in `meta.passthrough`:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// mycli checkout -- README.md
|
|
110
|
+
handler: (args, ctx, meta) => {
|
|
111
|
+
args.target; // "README.md" (assigned to positional arg)
|
|
112
|
+
meta.passthrough; // ["README.md"] (raw tokens after --)
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
72
116
|
### `just-bash-util/config` — Config file discovery
|
|
73
117
|
|
|
74
118
|
Cosmiconfig-style config search that walks up the directory tree, trying conventional filenames at each level. Comments and trailing commas are supported out of the box.
|
|
@@ -142,6 +186,15 @@ parsePackageSpecifier("@vue/shared/dist"); // { name: "@vue/shared", subpath: ".
|
|
|
142
186
|
parsePackageSpecifier("lodash/merge"); // { name: "lodash", subpath: "./merge" }
|
|
143
187
|
```
|
|
144
188
|
|
|
189
|
+
**`join` vs `resolve`** — `join` concatenates segments and normalizes; an absolute second argument is kept as-is (appended, not replacing). `resolve` processes right-to-left and stops at the first absolute path, like Node's `path.resolve` (but without prepending `cwd` when no absolute segment exists):
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
join("/repo", "/file.txt"); // "/repo/file.txt" — concatenates with /
|
|
193
|
+
resolve("/repo", "/file.txt"); // "/file.txt" — absolute segment wins
|
|
194
|
+
resolve("/repo", "file.txt"); // "/repo/file.txt"
|
|
195
|
+
resolve("a", "b"); // "a/b" — stays relative (no cwd)
|
|
196
|
+
```
|
|
197
|
+
|
|
145
198
|
## Peer dependencies
|
|
146
199
|
|
|
147
200
|
Requires [`just-bash`](https://www.npmjs.com/package/just-bash) ^2.9.6 — provides the `CommandContext` and `ExecResult` types used throughout.
|
|
@@ -9,9 +9,9 @@ var OptionBuilder = class _OptionBuilder {
|
|
|
9
9
|
describe(text) {
|
|
10
10
|
return new _OptionBuilder({ ...this._def, description: text });
|
|
11
11
|
}
|
|
12
|
-
/** Set a short alias (single character) */
|
|
13
|
-
short
|
|
14
|
-
return new _OptionBuilder({ ...this._def, short
|
|
12
|
+
/** Set a short alias (single character, e.g. "p" for -p) */
|
|
13
|
+
alias(short) {
|
|
14
|
+
return new _OptionBuilder({ ...this._def, short });
|
|
15
15
|
}
|
|
16
16
|
/** Set an environment variable fallback */
|
|
17
17
|
env(name) {
|
|
@@ -56,9 +56,9 @@ var FlagBuilder = class _FlagBuilder {
|
|
|
56
56
|
describe(text) {
|
|
57
57
|
return new _FlagBuilder({ ...this._def, description: text });
|
|
58
58
|
}
|
|
59
|
-
/** Set a short alias (single character) */
|
|
60
|
-
short
|
|
61
|
-
return new _FlagBuilder({ ...this._def, short
|
|
59
|
+
/** Set a short alias (single character, e.g. "v" for -v) */
|
|
60
|
+
alias(short) {
|
|
61
|
+
return new _FlagBuilder({ ...this._def, short });
|
|
62
62
|
}
|
|
63
63
|
/** Set a default value */
|
|
64
64
|
default(value) {
|
|
@@ -91,7 +91,9 @@ var ArgBuilder = class _ArgBuilder {
|
|
|
91
91
|
required: false
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
-
/** Mark as variadic — collects all remaining positionals into an array
|
|
94
|
+
/** Mark as variadic — collects all remaining positionals into an array.
|
|
95
|
+
* If the arg is already optional, element-level undefined is stripped
|
|
96
|
+
* (the optionality means "zero or more", not "elements can be undefined"). */
|
|
95
97
|
variadic() {
|
|
96
98
|
return new _ArgBuilder({
|
|
97
99
|
...this._def,
|
|
@@ -208,6 +210,7 @@ function parseArgs(options, argDefs, tokens, env) {
|
|
|
208
210
|
if (token === "--") {
|
|
209
211
|
i++;
|
|
210
212
|
while (i < tokens.length) {
|
|
213
|
+
positionals.push(tokens[i]);
|
|
211
214
|
passthrough.push(tokens[i]);
|
|
212
215
|
i++;
|
|
213
216
|
}
|
|
@@ -266,10 +269,14 @@ function parseArgs(options, argDefs, tokens, env) {
|
|
|
266
269
|
const ch = chars[j];
|
|
267
270
|
const entry = shortMap.get(ch);
|
|
268
271
|
if (!entry) {
|
|
272
|
+
const suggestions = [];
|
|
273
|
+
if (longMap.has(ch)) {
|
|
274
|
+
suggestions.push(`--${ch}`);
|
|
275
|
+
}
|
|
269
276
|
errors.push({
|
|
270
277
|
type: "unknown_option",
|
|
271
278
|
name: `-${ch}`,
|
|
272
|
-
suggestions
|
|
279
|
+
suggestions
|
|
273
280
|
});
|
|
274
281
|
continue;
|
|
275
282
|
}
|
|
@@ -307,6 +314,8 @@ function parseArgs(options, argDefs, tokens, env) {
|
|
|
307
314
|
errors.push({ type: "missing_required", name: argName, kind: "arg" });
|
|
308
315
|
} else if (argDef.default !== void 0) {
|
|
309
316
|
result[argName] = argDef.default;
|
|
317
|
+
} else {
|
|
318
|
+
result[argName] = [];
|
|
310
319
|
}
|
|
311
320
|
posIdx = positionals.length;
|
|
312
321
|
} else {
|
|
@@ -527,12 +536,8 @@ var Command = class _Command {
|
|
|
527
536
|
handler;
|
|
528
537
|
children = /* @__PURE__ */ new Map();
|
|
529
538
|
parent;
|
|
530
|
-
/** @internal — accumulated builder types for generic inference */
|
|
531
|
-
_accOpts;
|
|
532
|
-
/** @internal — args builder types for generic inference */
|
|
533
|
-
_accArgs;
|
|
534
539
|
/** @internal */
|
|
535
|
-
constructor(name, description, options, args, examples, omitInherited, handler
|
|
540
|
+
constructor(name, description, options, args, examples, omitInherited, handler) {
|
|
536
541
|
this.name = name;
|
|
537
542
|
this.description = description;
|
|
538
543
|
this.options = options;
|
|
@@ -540,8 +545,6 @@ var Command = class _Command {
|
|
|
540
545
|
this.examples = examples;
|
|
541
546
|
this.omitInherited = omitInherited;
|
|
542
547
|
this.handler = handler;
|
|
543
|
-
this._accOpts = accOpts;
|
|
544
|
-
this._accArgs = accArgs;
|
|
545
548
|
}
|
|
546
549
|
// --------------------------------------------------------------------------
|
|
547
550
|
// Tree building
|
|
@@ -549,9 +552,6 @@ var Command = class _Command {
|
|
|
549
552
|
/** Add a subcommand. Returns the child command for further nesting. */
|
|
550
553
|
command(name, config) {
|
|
551
554
|
const omitSet = new Set(config.omitInherited ?? []);
|
|
552
|
-
const parentAcc = { ...this._accOpts };
|
|
553
|
-
for (const key of omitSet) delete parentAcc[key];
|
|
554
|
-
const accOpts = { ...parentAcc, ...config.options ?? {} };
|
|
555
555
|
const child = new _Command(
|
|
556
556
|
name,
|
|
557
557
|
config.description,
|
|
@@ -559,9 +559,7 @@ var Command = class _Command {
|
|
|
559
559
|
resolveArgsInput(config.args),
|
|
560
560
|
config.examples ?? [],
|
|
561
561
|
omitSet,
|
|
562
|
-
config.handler
|
|
563
|
-
accOpts,
|
|
564
|
-
config.args ?? []
|
|
562
|
+
config.handler
|
|
565
563
|
);
|
|
566
564
|
child.parent = this;
|
|
567
565
|
this.children.set(name, child);
|
|
@@ -781,9 +779,7 @@ function command(name, config) {
|
|
|
781
779
|
resolveArgsInput(config.args),
|
|
782
780
|
config.examples ?? [],
|
|
783
781
|
/* @__PURE__ */ new Set(),
|
|
784
|
-
config.handler
|
|
785
|
-
config.options ?? {},
|
|
786
|
-
config.args ?? []
|
|
782
|
+
config.handler
|
|
787
783
|
);
|
|
788
784
|
}
|
|
789
785
|
function hasHelpFlag(tokens) {
|
package/dist/command/index.d.ts
CHANGED
|
@@ -74,8 +74,8 @@ declare class OptionBuilder<TOut, THasDefault extends boolean = false> {
|
|
|
74
74
|
constructor(def: OptionDef<TOut>);
|
|
75
75
|
/** Add a description */
|
|
76
76
|
describe(text: string): OptionBuilder<TOut, THasDefault>;
|
|
77
|
-
/** Set a short alias (single character) */
|
|
78
|
-
short
|
|
77
|
+
/** Set a short alias (single character, e.g. "p" for -p) */
|
|
78
|
+
alias(short: string): OptionBuilder<TOut, THasDefault>;
|
|
79
79
|
/** Set an environment variable fallback */
|
|
80
80
|
env(name: string): OptionBuilder<TOut, THasDefault>;
|
|
81
81
|
/** Mark as required — removes undefined from TOut */
|
|
@@ -94,8 +94,8 @@ declare class FlagBuilder {
|
|
|
94
94
|
constructor(def?: FlagDef);
|
|
95
95
|
/** Add a description */
|
|
96
96
|
describe(text: string): FlagBuilder;
|
|
97
|
-
/** Set a short alias (single character) */
|
|
98
|
-
short
|
|
97
|
+
/** Set a short alias (single character, e.g. "v" for -v) */
|
|
98
|
+
alias(short: string): FlagBuilder;
|
|
99
99
|
/** Set a default value */
|
|
100
100
|
default(value: boolean): FlagBuilder;
|
|
101
101
|
}
|
|
@@ -110,8 +110,10 @@ declare class ArgBuilder<TOut, TName extends string = never, THasDefault extends
|
|
|
110
110
|
describe(text: string): ArgBuilder<TOut, TName, THasDefault>;
|
|
111
111
|
/** Mark as optional — adds undefined to TOut */
|
|
112
112
|
optional(): ArgBuilder<TOut | undefined, TName, THasDefault>;
|
|
113
|
-
/** Mark as variadic — collects all remaining positionals into an array
|
|
114
|
-
|
|
113
|
+
/** Mark as variadic — collects all remaining positionals into an array.
|
|
114
|
+
* If the arg is already optional, element-level undefined is stripped
|
|
115
|
+
* (the optionality means "zero or more", not "elements can be undefined"). */
|
|
116
|
+
variadic(): ArgBuilder<NonNullable<TOut>[], TName, THasDefault>;
|
|
115
117
|
/** Set a default value (also makes the arg optional at parse time) */
|
|
116
118
|
default(value: TOut): ArgBuilder<TOut, TName, true>;
|
|
117
119
|
}
|
|
@@ -161,7 +163,7 @@ type InferInvokeArgs<T extends ArgsInput> = {
|
|
|
161
163
|
} & {
|
|
162
164
|
[I in keyof T & `${number}` as T[I] extends ArgBuilder<infer _V, infer N extends string, infer D> ? [D] extends [true] ? N : T[I] extends ArgBuilder<infer V, any, any> ? undefined extends V ? N : never : never : never]?: T[I] extends ArgBuilder<infer V, any, any> ? V : never;
|
|
163
165
|
};
|
|
164
|
-
declare class Command<
|
|
166
|
+
declare class Command<THandlerArgs extends object = {}, TInvokeArgs extends object = {}> {
|
|
165
167
|
readonly name: string;
|
|
166
168
|
readonly description: string;
|
|
167
169
|
readonly options: OptionsSchema;
|
|
@@ -171,12 +173,12 @@ declare class Command<TAccOpts extends OptionsInput = {}, TAccArgs extends ArgsI
|
|
|
171
173
|
readonly handler?: Handler<any>;
|
|
172
174
|
readonly children: Map<string, Command<any, any>>;
|
|
173
175
|
parent?: Command<any, any>;
|
|
174
|
-
/** @internal —
|
|
175
|
-
readonly
|
|
176
|
-
/** @internal —
|
|
177
|
-
readonly
|
|
176
|
+
/** @internal — phantom type carrying the resolved handler args */
|
|
177
|
+
readonly _handlerArgs: THandlerArgs;
|
|
178
|
+
/** @internal — phantom type carrying the resolved invoke args */
|
|
179
|
+
readonly _invokeArgs: TInvokeArgs;
|
|
178
180
|
/** @internal */
|
|
179
|
-
constructor(name: string, description: string, options: OptionsSchema, args: ArgsSchema, examples: readonly string[], omitInherited: ReadonlySet<string>, handler: Handler<any> | undefined
|
|
181
|
+
constructor(name: string, description: string, options: OptionsSchema, args: ArgsSchema, examples: readonly string[], omitInherited: ReadonlySet<string>, handler: Handler<any> | undefined);
|
|
180
182
|
/** Add a subcommand. Returns the child command for further nesting. */
|
|
181
183
|
command<TOpts extends OptionsInput = {}, const TArgs extends ArgsInput = [], const TOmit extends string[] = []>(name: string, config: {
|
|
182
184
|
readonly description: string;
|
|
@@ -184,8 +186,8 @@ declare class Command<TAccOpts extends OptionsInput = {}, TAccArgs extends ArgsI
|
|
|
184
186
|
readonly args?: TArgs;
|
|
185
187
|
readonly examples?: readonly string[];
|
|
186
188
|
readonly omitInherited?: TOmit;
|
|
187
|
-
readonly handler?: Handler<Prettify<Omit<
|
|
188
|
-
}): Command<Omit<
|
|
189
|
+
readonly handler?: Handler<Prettify<Omit<THandlerArgs, TOmit[number]> & InferOptionsFromInput<TOpts> & InferArgsFromInput<TArgs>>>;
|
|
190
|
+
}): Command<Prettify<Omit<THandlerArgs, TOmit[number]> & InferOptionsFromInput<TOpts> & InferArgsFromInput<TArgs>>, Prettify<Omit<TInvokeArgs, TOmit[number]> & InferInvokeOptions<TOpts> & InferInvokeArgs<TArgs>>>;
|
|
189
191
|
/** Full path from root (e.g. "mycli db migrate") */
|
|
190
192
|
get fullPath(): string;
|
|
191
193
|
/**
|
|
@@ -221,7 +223,7 @@ declare class Command<TAccOpts extends OptionsInput = {}, TAccArgs extends ArgsI
|
|
|
221
223
|
* await cli.execute(["serve", ...tokens], ctx);
|
|
222
224
|
* ```
|
|
223
225
|
*/
|
|
224
|
-
toTokens(args: Partial<
|
|
226
|
+
toTokens(args: Partial<THandlerArgs>): string[];
|
|
225
227
|
/**
|
|
226
228
|
* Call this command's handler directly with typed args.
|
|
227
229
|
*
|
|
@@ -234,7 +236,7 @@ declare class Command<TAccOpts extends OptionsInput = {}, TAccArgs extends ArgsI
|
|
|
234
236
|
* const result = await serve.invoke({ port: 8080, entry: "app.ts" }, ctx);
|
|
235
237
|
* ```
|
|
236
238
|
*/
|
|
237
|
-
invoke(args:
|
|
239
|
+
invoke(args: TInvokeArgs, ctx: CommandContext): Promise<ExecResult>;
|
|
238
240
|
/**
|
|
239
241
|
* Execute this command tree with the given tokens.
|
|
240
242
|
*
|
|
@@ -251,7 +253,7 @@ declare function command<TOpts extends OptionsInput = {}, const TArgs extends Ar
|
|
|
251
253
|
readonly args?: TArgs;
|
|
252
254
|
readonly examples?: readonly string[];
|
|
253
255
|
readonly handler?: Handler<Prettify<InferOptionsFromInput<TOpts> & InferArgsFromInput<TArgs>>>;
|
|
254
|
-
}): Command<TOpts
|
|
256
|
+
}): Command<Prettify<InferOptionsFromInput<TOpts> & InferArgsFromInput<TArgs>>, Prettify<InferInvokeOptions<TOpts> & InferInvokeArgs<TArgs>>>;
|
|
255
257
|
/**
|
|
256
258
|
* Infer the handler args type from a Command instance.
|
|
257
259
|
*
|
|
@@ -270,7 +272,7 @@ declare function command<TOpts extends OptionsInput = {}, const TArgs extends Ar
|
|
|
270
272
|
* // ^? { port: number; entry: string }
|
|
271
273
|
* ```
|
|
272
274
|
*/
|
|
273
|
-
type Infer<T extends Command<any, any>> =
|
|
275
|
+
type Infer<T extends Command<any, any>> = T["_handlerArgs"];
|
|
274
276
|
|
|
275
277
|
type ParseArgsResult = {
|
|
276
278
|
ok: true;
|
package/dist/command/index.js
CHANGED
package/dist/index.js
CHANGED