commander-wizard 0.0.1 → 0.0.3
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 +11 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/wizard.d.ts +0 -3
- package/dist/wizard.js +47 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Save as `cli.mjs`:
|
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
19
|
import { Command } from 'commander';
|
|
20
|
-
import { addWizard
|
|
20
|
+
import { addWizard } from 'commander-wizard';
|
|
21
21
|
|
|
22
22
|
const program = new Command('deploy-cli');
|
|
23
23
|
const deploy = program.command('deploy')
|
|
@@ -30,11 +30,7 @@ const deploy = program.command('deploy')
|
|
|
30
30
|
// Add your commands and options before calling addWizard.
|
|
31
31
|
addWizard(program, { invocation: ['node', 'cli.mjs'] });
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
await program.parseAsync();
|
|
35
|
-
} catch (error) {
|
|
36
|
-
if (!(error instanceof WizardCancelledError)) throw error;
|
|
37
|
-
}
|
|
33
|
+
await program.parseAsync();
|
|
38
34
|
```
|
|
39
35
|
|
|
40
36
|
```sh
|
|
@@ -68,9 +64,10 @@ After confirmation, Commander applies parsers, requirements, conflicts, and
|
|
|
68
64
|
implications before running your action. Restart the wizard to correct invalid
|
|
69
65
|
inputs; custom parsers do not run during prompting.
|
|
70
66
|
|
|
71
|
-
Declining or pressing Ctrl-C
|
|
72
|
-
hooks or action.
|
|
73
|
-
`exitOverride()`
|
|
67
|
+
Declining or pressing Ctrl-C exits cleanly with code 0 without running your
|
|
68
|
+
hooks or action. Wizard failures print like Commander errors and exit 1. For
|
|
69
|
+
tests and embedding, configure `exitOverride()` to catch everything instead
|
|
70
|
+
of exiting — the cancellation code is `commander-wizard.cancelled`.
|
|
74
71
|
|
|
75
72
|
You keep Commander's parsing and validation for ordinary invocations. Your
|
|
76
73
|
action receives no wizard-trigger option after a wizard run.
|
|
@@ -129,8 +126,9 @@ a numeric default alone does not perform that conversion.
|
|
|
129
126
|
|
|
130
127
|
## Compatibility limits
|
|
131
128
|
|
|
132
|
-
Use
|
|
133
|
-
|
|
129
|
+
Use global scalar options, short flags, positive/negative boolean pairs,
|
|
130
|
+
positional arguments, choices, and leaf variadics. Actions are optional:
|
|
131
|
+
commands that read `.opts()` after parsing work unchanged in wizard mode.
|
|
134
132
|
|
|
135
133
|
In wizard mode, put the full command path before flags:
|
|
136
134
|
`cli group command --wizard --flag=value`. Keep short flags separate; avoid `-abc` and
|
|
@@ -138,8 +136,8 @@ In wizard mode, put the full command path before flags:
|
|
|
138
136
|
|
|
139
137
|
You cannot use these Commander features in wizard mode:
|
|
140
138
|
|
|
141
|
-
- Executable subcommands,
|
|
142
|
-
children
|
|
139
|
+
- Executable subcommands, implicit/default subcommands, or targeting commands
|
|
140
|
+
with children.
|
|
143
141
|
- Ancestor positional arguments or variadic options, positional/pass-through
|
|
144
142
|
option modes, or shadowed global option names/flags.
|
|
145
143
|
- Environment-bound options, optional option values (`--color [value]`), presets,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { addWizard
|
|
1
|
+
export { addWizard } from './wizard.js';
|
|
2
2
|
export type { WizardOptions } from './wizard.js';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { addWizard
|
|
1
|
+
export { addWizard } from './wizard.js';
|
package/dist/wizard.d.ts
CHANGED
package/dist/wizard.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import { inspect } from 'node:util';
|
|
3
|
-
|
|
3
|
+
class WizardError extends Error {
|
|
4
|
+
}
|
|
5
|
+
/** Aborts collection on cancel; surfaced to callers through Commander's exit channel. */
|
|
6
|
+
class WizardCancelledError extends Error {
|
|
4
7
|
constructor() { super('Wizard cancelled.'); this.name = 'WizardCancelledError'; }
|
|
5
8
|
}
|
|
6
9
|
const installed = new WeakSet();
|
|
7
|
-
const fail = (message) => { throw new
|
|
10
|
+
const fail = (message) => { throw new WizardError(`Wizard: ${message}`); };
|
|
8
11
|
/** Decorate an already-configured program. No global/prototype patching; use parseAsync for wizard mode. */
|
|
9
12
|
export function addWizard(program, config = {}) {
|
|
10
13
|
if (installed.has(program))
|
|
@@ -37,29 +40,40 @@ export function addWizard(program, config = {}) {
|
|
|
37
40
|
cmd.on(`option:${flagOption.name()}`, () => fail('use parseAsync() with an explicit command path and unbundled flags for wizard mode.'));
|
|
38
41
|
}
|
|
39
42
|
program.parseAsync = async function (argv, options) {
|
|
40
|
-
const args = userArgs(argv, options?.from);
|
|
41
|
-
if (!requested(args, markers))
|
|
42
|
-
return await parseAsync.call(this, argv, options);
|
|
43
|
-
if (options?.from === 'electron' || (!argv && process.versions.electron))
|
|
44
|
-
fail('Electron wizard invocations are unsupported; pass explicit user arguments.');
|
|
45
|
-
let input;
|
|
46
43
|
try {
|
|
47
|
-
|
|
44
|
+
const args = userArgs(argv, options?.from);
|
|
45
|
+
if (!requested(args, markers))
|
|
46
|
+
return await parseAsync.call(this, argv, options);
|
|
47
|
+
if (options?.from === 'electron' || (!argv && process.versions.electron))
|
|
48
|
+
fail('Electron wizard invocations are unsupported; pass explicit user arguments.');
|
|
49
|
+
let input;
|
|
50
|
+
try {
|
|
51
|
+
input = scan(program, args, markers);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Commander can distinguish reserved text used as data in grammars we do not support.
|
|
55
|
+
// An actual wizard option is stopped by the option listener above.
|
|
56
|
+
return await parseAsync.call(this, argv, options);
|
|
57
|
+
}
|
|
58
|
+
// A marker consumed as an option value is data, not a wizard request.
|
|
59
|
+
if (!input.wizard)
|
|
60
|
+
return await parseAsync.call(this, argv, options);
|
|
61
|
+
checkLayout(input.chain, wizardKey);
|
|
62
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
63
|
+
fail('interactive input requires a TTY.');
|
|
64
|
+
const completed = await collect(input, config, wizardKey);
|
|
65
|
+
// Commander alone owns coercion, validation, hooks, and action dispatch.
|
|
66
|
+
return await parseAsync.call(this, completed, { from: 'user' });
|
|
48
67
|
}
|
|
49
|
-
catch {
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Route our failures through Commander's own channel: stock CLI behavior by default,
|
|
70
|
+
// catchable via .exitOverride() for tests and embedding — no library-specific catches.
|
|
71
|
+
if (error instanceof WizardCancelledError)
|
|
72
|
+
exitVia(this, 0, 'commander-wizard.cancelled', 'Wizard cancelled.');
|
|
73
|
+
if (error instanceof WizardError)
|
|
74
|
+
this.error(error.message);
|
|
75
|
+
throw error;
|
|
53
76
|
}
|
|
54
|
-
// A marker consumed as an option value is data, not a wizard request.
|
|
55
|
-
if (!input.wizard)
|
|
56
|
-
return await parseAsync.call(this, argv, options);
|
|
57
|
-
checkLayout(input.chain, wizardKey);
|
|
58
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
59
|
-
fail('interactive input requires a TTY.');
|
|
60
|
-
const completed = await collect(input, config, wizardKey);
|
|
61
|
-
// Commander alone owns coercion, validation, hooks, and action dispatch.
|
|
62
|
-
return await parseAsync.call(this, completed, { from: 'user' });
|
|
63
77
|
};
|
|
64
78
|
installed.add(program);
|
|
65
79
|
return program;
|
|
@@ -155,8 +169,9 @@ function checkLayout(chain, wizardKey) {
|
|
|
155
169
|
}
|
|
156
170
|
}
|
|
157
171
|
const leaf = chain.at(-1);
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
// No action requirement: parse-then-.opts() CLIs work unchanged — the re-parse validates and populates.
|
|
173
|
+
if (leaf.commands.length)
|
|
174
|
+
fail('select an explicit leaf command; commands with children are ambiguous in wizard mode.');
|
|
160
175
|
}
|
|
161
176
|
function defaults(owner, config) {
|
|
162
177
|
if (owner.defaultValue === undefined)
|
|
@@ -309,7 +324,8 @@ async function collect(input, config, wizardKey) {
|
|
|
309
324
|
summary.push(`${arg.name()}: ${inspect(arg.variadic ? values : values[0])}`);
|
|
310
325
|
}
|
|
311
326
|
positional.push(...input.positionals.slice(cursor)); // let Commander report excess arguments
|
|
312
|
-
|
|
327
|
+
if (positional.length)
|
|
328
|
+
argv.push('--', ...positional);
|
|
313
329
|
const invocation = config.invocation ?? [process.execPath, ...process.execArgv, process.argv[1] ?? fail('provide invocation.')];
|
|
314
330
|
const tokens = [...invocation, ...argv];
|
|
315
331
|
if (tokens.some(token => token.includes('\0')))
|
|
@@ -345,6 +361,12 @@ function unwrap(value) {
|
|
|
345
361
|
}
|
|
346
362
|
return value;
|
|
347
363
|
}
|
|
364
|
+
/** Exits through Commander's own channel so .exitOverride() stays authoritative. */
|
|
365
|
+
function exitVia(program, exitCode, code, message) {
|
|
366
|
+
const exit = Reflect.get(program, '_exit');
|
|
367
|
+
exit.call(program, exitCode, code, message);
|
|
368
|
+
throw new Error('unreachable: _exit exits, or an exitOverride callback throws');
|
|
369
|
+
}
|
|
348
370
|
/** POSIX shell quoting. Windows shells are not supported. */
|
|
349
371
|
function shellQuote(value) {
|
|
350
372
|
return /^[\w.,:/@%+=-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
|