commander-wizard 0.0.1 → 0.0.2
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 +6 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/wizard.d.ts +0 -3
- package/dist/wizard.js +42 -22
- 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.
|
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;
|
|
@@ -345,6 +359,12 @@ function unwrap(value) {
|
|
|
345
359
|
}
|
|
346
360
|
return value;
|
|
347
361
|
}
|
|
362
|
+
/** Exits through Commander's own channel so .exitOverride() stays authoritative. */
|
|
363
|
+
function exitVia(program, exitCode, code, message) {
|
|
364
|
+
const exit = Reflect.get(program, '_exit');
|
|
365
|
+
exit.call(program, exitCode, code, message);
|
|
366
|
+
throw new Error('unreachable: _exit exits, or an exitOverride callback throws');
|
|
367
|
+
}
|
|
348
368
|
/** POSIX shell quoting. Windows shells are not supported. */
|
|
349
369
|
function shellQuote(value) {
|
|
350
370
|
return /^[\w.,:/@%+=-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
|