@gunshi/definition 1.0.0-rc.5 → 1.0.0-rc.6
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 +3 -0
- package/lib/index.d.ts +222 -18
- package/lib/index.js +248 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,6 +9,9 @@ This package exports the bellow APIs and types.
|
|
|
9
9
|
|
|
10
10
|
- `define`: A function to define a command.
|
|
11
11
|
- `lazy`: A function to lazily load a command.
|
|
12
|
+
- `CommandError` and `isCommandError`: An error for the failures that a command expects, printed without a stack trace, and its guard.
|
|
13
|
+
- `jsonArgs`: The argument that a command spreads into its `args` to support `--json`.
|
|
14
|
+
- `SubCommandable`: The type of a command to collect into the sub-commands, with or without `jsonArgs`.
|
|
12
15
|
- Some basic type definitions, such as `Command`, `LazyCommand`, etc.
|
|
13
16
|
|
|
14
17
|
<!-- eslint-disable markdown/no-missing-label-refs -->
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
//#region ../gunshi/src/constants.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The key of the mark on the `json` argument of `jsonArgs`. A string key, not a symbol: `gunshi` and the other
|
|
4
|
+
* packages bundle their own copies of the types, and each copy of a `unique symbol` is a different type.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
declare const JSON_ARG = "~gunshi.json-arg";
|
|
9
|
+
//#endregion
|
|
1
10
|
//#region ../../node_modules/.pnpm/args-tokens@1.0.2/node_modules/args-tokens/lib/parser-DvPxBZG9.d.ts
|
|
2
11
|
/**
|
|
3
12
|
* Entry point of argument parser.
|
|
@@ -730,7 +739,7 @@ interface PluginContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
|
|
|
730
739
|
* @param name - Command name
|
|
731
740
|
* @param command - Command definition
|
|
732
741
|
*/
|
|
733
|
-
addCommand(name: string, command:
|
|
742
|
+
addCommand(name: string, command: SubCommandable): void;
|
|
734
743
|
/**
|
|
735
744
|
* Check if a command exists.
|
|
736
745
|
*
|
|
@@ -771,7 +780,7 @@ interface PluginContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
|
|
|
771
780
|
*
|
|
772
781
|
* @param decorator - A decorator function that wraps the command runner
|
|
773
782
|
*/
|
|
774
|
-
decorateCommand<L extends Record<string, unknown> = DefaultGunshiParams['extensions']>(decorator: (baseRunner: (ctx: Readonly<CommandContext<MergeGunshiExtensions<G, L>>>) => Awaitable<void | string>) => (ctx: Readonly<CommandContext<MergeGunshiExtensions<G, L>>>) => Awaitable<void | string>): void;
|
|
783
|
+
decorateCommand<L extends Record<string, unknown> = DefaultGunshiParams['extensions']>(decorator: (baseRunner: (ctx: Readonly<CommandContext<MergeGunshiExtensions<G, L>>>) => Awaitable<void | string | JsonResult>) => (ctx: Readonly<CommandContext<MergeGunshiExtensions<G, L>>>) => Awaitable<void | string | JsonResult>): void;
|
|
775
784
|
}
|
|
776
785
|
//#endregion
|
|
777
786
|
//#region ../gunshi/src/plugin/core.d.ts
|
|
@@ -1008,7 +1017,11 @@ interface CommandEnvironment<G extends GunshiParamsConstraint = DefaultGunshiPar
|
|
|
1008
1017
|
* (they are still returned as strings) and to silence {@linkcode CommandContext.log}.
|
|
1009
1018
|
*
|
|
1010
1019
|
* Output that must still appear — machine-readable results for an agent, warnings for the
|
|
1011
|
-
* CLI author — should use `console.log` / `console.warn`, not `ctx.log`.
|
|
1020
|
+
* CLI author — should use `console.log` / `console.warn`, not `ctx.log`. A command that spreads
|
|
1021
|
+
* `jsonArgs` and is given `--json` prints its result, and its errors, as JSON even with it.
|
|
1022
|
+
*
|
|
1023
|
+
* The validation errors, and a `CommandError` that the command throws, are not printed either, so
|
|
1024
|
+
* `cli()` rejects with them, for the caller to report them.
|
|
1012
1025
|
*
|
|
1013
1026
|
* @default false
|
|
1014
1027
|
* @see {@linkcode CliOptions.usageSilent}
|
|
@@ -1068,7 +1081,8 @@ interface CommandEnvironment<G extends GunshiParamsConstraint = DefaultGunshiPar
|
|
|
1068
1081
|
*/
|
|
1069
1082
|
onBeforeCommand: ((ctx: Readonly<CommandContext<G>>) => Awaitable<void>) | undefined;
|
|
1070
1083
|
/**
|
|
1071
|
-
* Hook that runs after successful command execution
|
|
1084
|
+
* Hook that runs after successful command execution. It receives the result of the command only
|
|
1085
|
+
* when it is a string.
|
|
1072
1086
|
*
|
|
1073
1087
|
* @see {@linkcode CliOptions.onAfterCommand}
|
|
1074
1088
|
* @since v0.27.0
|
|
@@ -1133,10 +1147,12 @@ interface CliOptions<G extends GunshiParamsConstraint = DefaultGunshiParams> {
|
|
|
1133
1147
|
* (they are still returned as strings) and to silence {@linkcode CommandContext.log}.
|
|
1134
1148
|
*
|
|
1135
1149
|
* Output that must still appear — machine-readable results for an agent, warnings for the
|
|
1136
|
-
* CLI author — should use `console.log` / `console.warn`, not `ctx.log`.
|
|
1150
|
+
* CLI author — should use `console.log` / `console.warn`, not `ctx.log`. A command that spreads
|
|
1151
|
+
* `jsonArgs` and is given `--json` prints its result, and its errors, as JSON even with it.
|
|
1137
1152
|
*
|
|
1138
|
-
* The validation errors
|
|
1139
|
-
* report them (see
|
|
1153
|
+
* The validation errors, and a `CommandError` that the command throws, are not printed either, so
|
|
1154
|
+
* `cli()` rejects with them, for the caller to report them (see
|
|
1155
|
+
* {@linkcode CliOptions.renderValidationErrors}). The exit code is left alone.
|
|
1140
1156
|
*
|
|
1141
1157
|
* @default false
|
|
1142
1158
|
*/
|
|
@@ -1148,6 +1164,10 @@ interface CliOptions<G extends GunshiParamsConstraint = DefaultGunshiParams> {
|
|
|
1148
1164
|
* arguments or installed global options are reported as validation errors
|
|
1149
1165
|
* before the command runner is executed.
|
|
1150
1166
|
*
|
|
1167
|
+
* `--json` is not reported, since gunshi knows what it means: given to a command that does not
|
|
1168
|
+
* spread `jsonArgs`, the global options plugin reports `json_not_supported` for it instead, with or
|
|
1169
|
+
* without `strict`. `--json=false` asks for no JSON, and is reported.
|
|
1170
|
+
*
|
|
1151
1171
|
* @default false
|
|
1152
1172
|
*/
|
|
1153
1173
|
strict?: boolean;
|
|
@@ -1167,6 +1187,9 @@ interface CliOptions<G extends GunshiParamsConstraint = DefaultGunshiParams> {
|
|
|
1167
1187
|
* Set it to `null` to report the validation errors yourself: nothing is printed, and `cli()` rejects
|
|
1168
1188
|
* with the `AggregateError`. A renderer that returns an empty message, and
|
|
1169
1189
|
* {@linkcode CliOptions.usageSilent}, also leave them to the caller.
|
|
1190
|
+
*
|
|
1191
|
+
* With `--json`, the plugin prints the validation errors as one line of JSON, and does not call
|
|
1192
|
+
* the renderer. `null` and {@linkcode CliOptions.usageSilent} still leave them to the caller.
|
|
1170
1193
|
*/
|
|
1171
1194
|
renderValidationErrors?: ((ctx: Readonly<CommandContext<G>>, error: AggregateError) => Promise<string>) | null;
|
|
1172
1195
|
/**
|
|
@@ -1193,15 +1216,17 @@ interface CliOptions<G extends GunshiParamsConstraint = DefaultGunshiParams> {
|
|
|
1193
1216
|
* Hook that runs after successful command execution
|
|
1194
1217
|
*
|
|
1195
1218
|
* @param ctx - The command context
|
|
1196
|
-
* @param result - The command
|
|
1219
|
+
* @param result - The result of the command, when it is a string. The result of a command that
|
|
1220
|
+
* prints JSON with `--json` is printed, not passed, so it is `undefined`
|
|
1197
1221
|
* @since v0.27.0
|
|
1198
1222
|
*/
|
|
1199
1223
|
onAfterCommand?: (ctx: Readonly<CommandContext<G>>, result: string | undefined) => Awaitable<void>;
|
|
1200
1224
|
/**
|
|
1201
1225
|
* Hook that runs when a command throws an error, or when the arguments fail validation
|
|
1202
1226
|
*
|
|
1203
|
-
* For the validation errors that the global options plugin has printed, the
|
|
1204
|
-
* when the hook runs
|
|
1227
|
+
* For the validation errors and a `CommandError` that the global options plugin has printed, the
|
|
1228
|
+
* exit code is already set when the hook runs (`1` for the validation errors, and the `exitCode` of
|
|
1229
|
+
* a `CommandError`), so the hook can set another one, and `cli()` resolves after it. Any other
|
|
1205
1230
|
* error rejects `cli()` after the hook.
|
|
1206
1231
|
*
|
|
1207
1232
|
* @param ctx - The command context
|
|
@@ -1301,9 +1326,11 @@ interface CommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams>
|
|
|
1301
1326
|
*/
|
|
1302
1327
|
toKebab?: boolean;
|
|
1303
1328
|
/**
|
|
1304
|
-
* Output a message.
|
|
1329
|
+
* Output a message for people.
|
|
1305
1330
|
*
|
|
1306
|
-
* If {@linkcode CommandEnvironment.usageSilent} is true, the message is not output.
|
|
1331
|
+
* If {@linkcode CommandEnvironment.usageSilent} is true, the message is not output. Nor is it when a
|
|
1332
|
+
* command that spreads `jsonArgs` is asked for JSON with `--json`, so that stdout has the JSON only:
|
|
1333
|
+
* the hooks and the plugins get the same function.
|
|
1307
1334
|
*
|
|
1308
1335
|
* @param message - an output message, see {@linkcode console.log}
|
|
1309
1336
|
* @param optionalParams - an optional parameters, see {@linkcode console.log}
|
|
@@ -1547,9 +1574,40 @@ type CommandExamplesFetcher<G extends GunshiParamsConstraint = DefaultGunshiPara
|
|
|
1547
1574
|
* @typeParam G - A type extending {@linkcode GunshiParams} to specify the shape of command context.
|
|
1548
1575
|
*
|
|
1549
1576
|
* @param ctx - A {@link CommandContext | command context}
|
|
1550
|
-
* @returns
|
|
1577
|
+
* @returns The result of a command that spreads `jsonArgs`, an object keyed by a noun, which is printed
|
|
1578
|
+
* as JSON with `--json`. For any other command, a string, which `cli()` returns, or nothing. See
|
|
1579
|
+
* {@linkcode CommandRunnerResult}.
|
|
1580
|
+
*/
|
|
1581
|
+
type CommandRunner<G extends GunshiParamsConstraint = DefaultGunshiParams> = (ctx: Readonly<CommandContext<G>>) => CommandRunnerResult<G>;
|
|
1582
|
+
/**
|
|
1583
|
+
* The result of a command that supports `--json`: an object keyed by a noun, such as
|
|
1584
|
+
* `{ project }` or `{ projects }`, so that fields can be added later. The global options plugin prints
|
|
1585
|
+
* it with `JSON.stringify`, so convert what JSON cannot hold, such as a `Map` or a `BigInt`, yourself.
|
|
1586
|
+
*/
|
|
1587
|
+
type JsonResult = Record<string, unknown>;
|
|
1588
|
+
/**
|
|
1589
|
+
* Whether the arguments have the `json` of `jsonArgs`.
|
|
1590
|
+
*
|
|
1591
|
+
* @typeParam A - The arguments of a command
|
|
1592
|
+
*/
|
|
1593
|
+
type HasJsonArgs<A> = [A] extends [{
|
|
1594
|
+
readonly json: {
|
|
1595
|
+
readonly [JSON_ARG]: true;
|
|
1596
|
+
};
|
|
1597
|
+
}] ? true : false;
|
|
1598
|
+
/**
|
|
1599
|
+
* What the runner of a command returns: its result for a command that spreads `jsonArgs`, otherwise a string or
|
|
1600
|
+
* nothing.
|
|
1601
|
+
*
|
|
1602
|
+
* @typeParam G - A type extending {@linkcode GunshiParams}
|
|
1551
1603
|
*/
|
|
1552
|
-
type
|
|
1604
|
+
type CommandRunnerResult<G extends GunshiParamsConstraint = DefaultGunshiParams> = IsAnyArgs<ExtractArgs<G>> extends true ? Awaitable<string | void | JsonResult> : HasJsonArgs<ExtractArgs<G>> extends true ? Awaitable<JsonResult> : Awaitable<string | void>;
|
|
1605
|
+
/**
|
|
1606
|
+
* Whether the arguments are `any`, as in `CommandRunner<any>`. Then a runner may return anything it could before.
|
|
1607
|
+
*
|
|
1608
|
+
* @internal
|
|
1609
|
+
*/
|
|
1610
|
+
type IsAnyArgs<A> = 0 extends 1 & A ? true : false;
|
|
1553
1611
|
/**
|
|
1554
1612
|
* Command loader.
|
|
1555
1613
|
*
|
|
@@ -1585,7 +1643,7 @@ interface CommandContextParams<G extends GunshiParams | {
|
|
|
1585
1643
|
args: Args;
|
|
1586
1644
|
} | {
|
|
1587
1645
|
extensions: ExtendContext;
|
|
1588
|
-
}, V extends ArgValues<ExtractArgs<G>>, C extends Command<G> | LazyCommand<G> = Command<G>, E extends Record<string, CommandContextExtension> = Record<string, CommandContextExtension>> {
|
|
1646
|
+
}, V extends ArgValues<ExtractArgs<G>>, C extends Command<G> | LazyCommand<G> | SubCommandable = Command<G>, E extends Record<string, CommandContextExtension> = Record<string, CommandContextExtension>> {
|
|
1589
1647
|
/**
|
|
1590
1648
|
* An arguments of target command
|
|
1591
1649
|
*/
|
|
@@ -1649,7 +1707,144 @@ interface CommandContextParams<G extends GunshiParams | {
|
|
|
1649
1707
|
* @param param - A {@link CommandContextParams | parameters} to create a command context.
|
|
1650
1708
|
* @returns A {@link CommandContext | command context}, which is readonly.
|
|
1651
1709
|
*/
|
|
1652
|
-
declare function createCommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams, V extends ArgValues<ExtractArgs<G>> = ArgValues<ExtractArgs<G>>, C extends Command<G> | LazyCommand<G> = Command<G>, E extends Record<string, CommandContextExtension> = {}>({ args, explicit, values, positionals, rest, argv, tokens, command, extensions, cliOptions, callMode, commandPath, omitted, validationError }: CommandContextParams<G, V, C, E>): Promise<CommandContextResult<G, E>>;
|
|
1710
|
+
declare function createCommandContext<G extends GunshiParamsConstraint = DefaultGunshiParams, V extends ArgValues<ExtractArgs<G>> = ArgValues<ExtractArgs<G>>, C extends Command<G> | LazyCommand<G> | SubCommandable = Command<G>, E extends Record<string, CommandContextExtension> = {}>({ args, explicit, values, positionals, rest, argv, tokens, command, extensions, cliOptions, callMode, commandPath, omitted, validationError }: CommandContextParams<G, V, C, E>): Promise<CommandContextResult<G, E>>;
|
|
1711
|
+
//#endregion
|
|
1712
|
+
//#region ../gunshi/src/error.d.ts
|
|
1713
|
+
/**
|
|
1714
|
+
* Options for {@link CommandError}. All of them are optional.
|
|
1715
|
+
*/
|
|
1716
|
+
type CommandErrorOptions = {
|
|
1717
|
+
/**
|
|
1718
|
+
* The name of the failure, for programs to read, such as `not_found`. snake_case is recommended.
|
|
1719
|
+
*
|
|
1720
|
+
* It is not printed to the user. With `--json`, it is the `reason` of the JSON error, as it is.
|
|
1721
|
+
*/
|
|
1722
|
+
code?: string;
|
|
1723
|
+
/**
|
|
1724
|
+
* The exit code of the process, an integer from 1 to 255. Defaults to 1, and any other value is
|
|
1725
|
+
* replaced with 1, since the command has failed.
|
|
1726
|
+
*/
|
|
1727
|
+
exitCode?: number;
|
|
1728
|
+
/**
|
|
1729
|
+
* The error that caused the failure.
|
|
1730
|
+
*
|
|
1731
|
+
* Its message is printed as is, on a `Caused by:` line after the message of the
|
|
1732
|
+
* {@link CommandError}, and so is the message of its own cause. With `--json`, the messages are in
|
|
1733
|
+
* `data.causes` of the JSON error. Do not pass an error whose message may contain a secret, such as
|
|
1734
|
+
* a token in a URL: write the message of the {@link CommandError} yourself instead.
|
|
1735
|
+
*/
|
|
1736
|
+
cause?: unknown;
|
|
1737
|
+
};
|
|
1738
|
+
/**
|
|
1739
|
+
* Error for a failure that a command expects, such as a project that does not exist.
|
|
1740
|
+
*
|
|
1741
|
+
* When a command throws it from `run`, or a plugin throws it from a command decorator, the global
|
|
1742
|
+
* options plugin prints its message to stderr, followed by a `Caused by:` line for each cause,
|
|
1743
|
+
* without a stack trace. It sets the exit code of the process to {@link CommandError.exitCode},
|
|
1744
|
+
* and `cli()` resolves with `undefined`. `onErrorCommand` is still called with the error, after the
|
|
1745
|
+
* exit code is set, so the hook can change it.
|
|
1746
|
+
*
|
|
1747
|
+
* It is not printed, and `cli()` rejects with it, in these cases:
|
|
1748
|
+
*
|
|
1749
|
+
* - `usageSilent` is set, for the caller to report it
|
|
1750
|
+
* - it is thrown from a hook, while a plugin extends the command context, or while a lazy command
|
|
1751
|
+
* loads, outside the command decorators
|
|
1752
|
+
* - the CLI runs without the global options plugin, as `gunshi/bone` does
|
|
1753
|
+
*
|
|
1754
|
+
* Any other error thrown from a command is not printed either, and `cli()` rejects with it.
|
|
1755
|
+
*
|
|
1756
|
+
* Each package of gunshi (`gunshi`, `@gunshi/plugin`, `@gunshi/definition`) bundles its own copy of
|
|
1757
|
+
* this class, so `instanceof` does not match an error from another copy. Use
|
|
1758
|
+
* {@link isCommandError} to tell it.
|
|
1759
|
+
*
|
|
1760
|
+
* @example
|
|
1761
|
+
* ```js
|
|
1762
|
+
* import { CommandError, define } from 'gunshi'
|
|
1763
|
+
*
|
|
1764
|
+
* export default define({
|
|
1765
|
+
* name: 'show',
|
|
1766
|
+
* args: { project: { type: 'positional', description: 'Project name' } },
|
|
1767
|
+
* run: async ctx => {
|
|
1768
|
+
* const project = await findProject(ctx.values.project)
|
|
1769
|
+
* if (!project) {
|
|
1770
|
+
* throw new CommandError(`Project ${ctx.values.project} not found`, { code: 'not_found' })
|
|
1771
|
+
* }
|
|
1772
|
+
* ctx.log(project.description)
|
|
1773
|
+
* }
|
|
1774
|
+
* })
|
|
1775
|
+
* ```
|
|
1776
|
+
*/
|
|
1777
|
+
declare class CommandError extends Error {
|
|
1778
|
+
/**
|
|
1779
|
+
* The name of the failure, for programs to read. See {@link CommandErrorOptions.code}.
|
|
1780
|
+
*/
|
|
1781
|
+
readonly code?: string;
|
|
1782
|
+
/**
|
|
1783
|
+
* The exit code of the process, an integer from 1 to 255.
|
|
1784
|
+
*/
|
|
1785
|
+
readonly exitCode: number;
|
|
1786
|
+
/**
|
|
1787
|
+
* Create an error for a failure that a command expects.
|
|
1788
|
+
*
|
|
1789
|
+
* @param message - The message printed to the user. It may have more than one line.
|
|
1790
|
+
* @param options - The code, the exit code and the cause of the failure
|
|
1791
|
+
*/
|
|
1792
|
+
constructor(message: string, options?: CommandErrorOptions);
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Check whether an error is a {@link CommandError}.
|
|
1796
|
+
*
|
|
1797
|
+
* An error from another copy of gunshi is recognized through the `Symbol.for('gunshi.CommandError')`
|
|
1798
|
+
* brand. It is not recognized by `error.name`, so an error of the app that is also named
|
|
1799
|
+
* `CommandError` is not taken for one.
|
|
1800
|
+
*
|
|
1801
|
+
* @param error - An unknown error
|
|
1802
|
+
* @returns `true` if the error is a {@link CommandError}
|
|
1803
|
+
*/
|
|
1804
|
+
declare function isCommandError(error: unknown): error is CommandError;
|
|
1805
|
+
//#endregion
|
|
1806
|
+
//#region ../gunshi/src/json.d.ts
|
|
1807
|
+
/**
|
|
1808
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
1809
|
+
* @license MIT
|
|
1810
|
+
*/
|
|
1811
|
+
/**
|
|
1812
|
+
* The argument that a command spreads into its `args` to support `--json`.
|
|
1813
|
+
*
|
|
1814
|
+
* A command that spreads it returns its result from `run`, as an object keyed by a noun, and prints
|
|
1815
|
+
* the output for people with `ctx.log`. With `--json`, the global options plugin silences `ctx.log`,
|
|
1816
|
+
* prints the result as one JSON document to stdout, and prints a failure as one line of JSON to
|
|
1817
|
+
* stderr. `--json` given to a command that does not spread it stops the command before it runs, with
|
|
1818
|
+
* `json_not_supported`.
|
|
1819
|
+
*
|
|
1820
|
+
* The `json` argument has a mark, by which gunshi tells it from a `json` argument of the command's
|
|
1821
|
+
* own, which gunshi does not touch. To give it a short name, spread it:
|
|
1822
|
+
* `{ ...jsonArgs.json, short: 'j' }`. A lazy command spreads it into its definition too.
|
|
1823
|
+
*
|
|
1824
|
+
* @example
|
|
1825
|
+
* ```js
|
|
1826
|
+
* import { define, jsonArgs } from 'gunshi'
|
|
1827
|
+
*
|
|
1828
|
+
* export default define({
|
|
1829
|
+
* name: 'list',
|
|
1830
|
+
* args: { ...jsonArgs },
|
|
1831
|
+
* run: async ctx => {
|
|
1832
|
+
* const projects = await fetchProjects()
|
|
1833
|
+
* for (const project of projects) {
|
|
1834
|
+
* ctx.log(project.name)
|
|
1835
|
+
* }
|
|
1836
|
+
* return { projects }
|
|
1837
|
+
* }
|
|
1838
|
+
* })
|
|
1839
|
+
* ```
|
|
1840
|
+
*/
|
|
1841
|
+
declare const jsonArgs: {
|
|
1842
|
+
readonly json: {
|
|
1843
|
+
readonly type: "boolean";
|
|
1844
|
+
readonly description: "Output the result as JSON";
|
|
1845
|
+
readonly "~gunshi.json-arg": true;
|
|
1846
|
+
};
|
|
1847
|
+
};
|
|
1653
1848
|
//#endregion
|
|
1654
1849
|
//#region ../gunshi/src/definition.d.ts
|
|
1655
1850
|
/**
|
|
@@ -1748,6 +1943,15 @@ type DefineWithTypesReturn<DefaultExtensions extends ExtendContext, DefaultArgs
|
|
|
1748
1943
|
* @since v0.27.0
|
|
1749
1944
|
*/
|
|
1750
1945
|
declare function defineWithTypes<G extends GunshiParamsConstraint>(): DefineWithTypesReturn<ExtractExtensions<G>, ExtractArgs<G>>;
|
|
1946
|
+
/**
|
|
1947
|
+
* A definition that does not spread `jsonArgs`. A definition that does is checked by the overload that ties the
|
|
1948
|
+
* runner to its arguments.
|
|
1949
|
+
*
|
|
1950
|
+
* @internal
|
|
1951
|
+
*/
|
|
1952
|
+
type WithoutJsonArgs<D> = D extends {
|
|
1953
|
+
args: infer A;
|
|
1954
|
+
} ? HasJsonArgs<A> extends true ? never : unknown : unknown;
|
|
1751
1955
|
/**
|
|
1752
1956
|
* Define a {@link LazyCommand | lazy command}.
|
|
1753
1957
|
*
|
|
@@ -1827,7 +2031,7 @@ declare function lazy<D extends {
|
|
|
1827
2031
|
* including when the lazy command is used as the CLI entry command.
|
|
1828
2032
|
* @returns A {@link LazyCommand | lazy command} that can be executed later
|
|
1829
2033
|
*/
|
|
1830
|
-
declare function lazy<G extends GunshiParamsConstraint = DefaultGunshiParams, D extends Partial<Command<G>> = Partial<Command<G>>>(loader: CommandLoader<G>, definition?: D): LazyCommand<G, D>;
|
|
2034
|
+
declare function lazy<G extends GunshiParamsConstraint = DefaultGunshiParams, D extends Partial<Command<G>> = Partial<Command<G>>>(loader: CommandLoader<G>, definition?: D & WithoutJsonArgs<D>): LazyCommand<G, D>;
|
|
1831
2035
|
/**
|
|
1832
2036
|
* Return type for lazyWithTypes
|
|
1833
2037
|
*
|
|
@@ -1872,4 +2076,4 @@ type LazyWithTypesReturn<FullG extends GunshiParamsConstraint> = <D extends Part
|
|
|
1872
2076
|
*/
|
|
1873
2077
|
declare function lazyWithTypes<G extends GunshiParamsConstraint>(): LazyWithTypesReturn<NormalizeToGunshiParams<G>>;
|
|
1874
2078
|
//#endregion
|
|
1875
|
-
export { type ArgSchema, type ArgValues, type Args, type Command, type CommandContextParams, type CommandLoader, type CommandRunner, type DefaultGunshiParams, type ExtendContext, type GunshiParams, type LazyCommand, createCommandContext, define, defineWithTypes, lazy, lazyWithTypes };
|
|
2079
|
+
export { type ArgSchema, type ArgValues, type Args, type Command, type CommandContextParams, CommandError, type CommandErrorOptions, type CommandLoader, type CommandRunner, type CommandRunnerResult, type DefaultGunshiParams, type ExtendContext, type GunshiParams, type JsonResult, type LazyCommand, type SubCommandable, createCommandContext, define, defineWithTypes, isCommandError, jsonArgs, lazy, lazyWithTypes };
|
package/lib/index.js
CHANGED
|
@@ -26,6 +26,13 @@ const CLI_OPTIONS_DEFAULT = {
|
|
|
26
26
|
plugins: void 0,
|
|
27
27
|
fallbackToEntry: false
|
|
28
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* The key of the mark on the `json` argument of `jsonArgs`. A string key, not a symbol: `gunshi` and the other
|
|
31
|
+
* packages bundle their own copies of the types, and each copy of a `unique symbol` is a different type.
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
const JSON_ARG = "~gunshi.json-arg";
|
|
29
36
|
//#endregion
|
|
30
37
|
//#region ../gunshi/src/utils.ts
|
|
31
38
|
/**
|
|
@@ -33,6 +40,51 @@ const CLI_OPTIONS_DEFAULT = {
|
|
|
33
40
|
* @license MIT
|
|
34
41
|
*/
|
|
35
42
|
/**
|
|
43
|
+
* Check whether an argument schema is the `json` argument of `jsonArgs`, by its mark.
|
|
44
|
+
*
|
|
45
|
+
* @param schema - An argument schema
|
|
46
|
+
* @returns `true` if the schema has the mark of `jsonArgs`
|
|
47
|
+
*/
|
|
48
|
+
function isJsonArg(schema) {
|
|
49
|
+
return typeof schema === "object" && schema !== null && schema["~gunshi.json-arg"] === true;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve whether `--json` asks a command for JSON.
|
|
53
|
+
*
|
|
54
|
+
* A command that spreads `jsonArgs` is asked for JSON when the resolved value of its `json` is `true`:
|
|
55
|
+
* with `--json`, or with a short name that the command gives it, but not with `--json=false`, nor with
|
|
56
|
+
* `--json` after `--`. The raw tokens are read only when they are given, and only when there is no
|
|
57
|
+
* value to read: when the command has no `json` argument, and when the core could not resolve the
|
|
58
|
+
* command and left the values empty. A `json` argument without the mark belongs to the command, and
|
|
59
|
+
* `--json` is never taken for a request then.
|
|
60
|
+
*
|
|
61
|
+
* @param args - The arguments of the command, the global options included
|
|
62
|
+
* @param values - The resolved values of the arguments
|
|
63
|
+
* @param tokens - The raw tokens of the arguments, to find a `--json` that no argument declares
|
|
64
|
+
* @returns How the command is asked for JSON, or `undefined` when it is not
|
|
65
|
+
*/
|
|
66
|
+
function resolveJsonRequest(args, values, tokens) {
|
|
67
|
+
const schema = Object.hasOwn(args, "json") ? args.json : void 0;
|
|
68
|
+
if (schema === void 0) return tokens !== void 0 && hasJsonOption(tokens) ? "undeclared" : void 0;
|
|
69
|
+
if (!isJsonArg(schema)) return;
|
|
70
|
+
if (values.json !== void 0) return values.json === true ? "declared" : void 0;
|
|
71
|
+
return tokens !== void 0 && hasJsonOption(tokens) ? "declared" : void 0;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check whether the raw tokens have a `--json` that asks for JSON: `--json` or `--json=true`, before
|
|
75
|
+
* `--`. Without a declaration, a short name of `--json` is not known.
|
|
76
|
+
*
|
|
77
|
+
* @param tokens - The raw tokens of the arguments
|
|
78
|
+
* @returns `true` if a `--json` asks for JSON
|
|
79
|
+
*/
|
|
80
|
+
function hasJsonOption(tokens) {
|
|
81
|
+
for (const token of tokens) {
|
|
82
|
+
if (token.kind === "option-terminator") return false;
|
|
83
|
+
if (token.kind === "option" && token.rawName === "--json" && (token.value === void 0 || token.value === "true")) return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
36
88
|
* Check if the given command is a {@link LazyCommand}.
|
|
37
89
|
*
|
|
38
90
|
* @param cmd - A command to check
|
|
@@ -124,6 +176,14 @@ async function createCommandContext({ args = {}, explicit = {}, values = {}, pos
|
|
|
124
176
|
/**
|
|
125
177
|
* create the command context
|
|
126
178
|
*/
|
|
179
|
+
/**
|
|
180
|
+
* NOTE(kazupon): a command that spreads `jsonArgs` and is asked for JSON prints its result as JSON, so
|
|
181
|
+
* the human output of `ctx.log` is silenced, as it is with `usageSilent`. The hooks and the decorators
|
|
182
|
+
* get the same context, so their `ctx.log` is silenced too. Only the resolved value of the `json`
|
|
183
|
+
* argument counts here, not the raw tokens: a command that does not support `--json` keeps its output,
|
|
184
|
+
* and the global options plugin reports the `--json` given to it.
|
|
185
|
+
*/
|
|
186
|
+
const silent = cliOptions.usageSilent || resolveJsonRequest(_args, values) === "declared";
|
|
127
187
|
const core = Object.assign(create(), {
|
|
128
188
|
name: getCommandName(command),
|
|
129
189
|
description: command.description,
|
|
@@ -139,7 +199,7 @@ async function createCommandContext({ args = {}, explicit = {}, values = {}, pos
|
|
|
139
199
|
_: argv,
|
|
140
200
|
tokens,
|
|
141
201
|
toKebab: command.toKebab,
|
|
142
|
-
log:
|
|
202
|
+
log: silent ? NOOP : log,
|
|
143
203
|
validationError
|
|
144
204
|
});
|
|
145
205
|
/**
|
|
@@ -165,6 +225,192 @@ function getCommandName(cmd) {
|
|
|
165
225
|
else if (typeof cmd === "object") return cmd.name || "(anonymous)";
|
|
166
226
|
else return ANONYMOUS_COMMAND_NAME;
|
|
167
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* forked from `nodejs/node` (`pkgjs/parseargs`)
|
|
230
|
+
* repository url: https://github.com/nodejs/node (https://github.com/pkgjs/parseargs)
|
|
231
|
+
* code url: https://github.com/nodejs/node/blob/main/lib/internal/util/parse_args/parse_args.js
|
|
232
|
+
*
|
|
233
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
234
|
+
* @license MIT
|
|
235
|
+
*/
|
|
236
|
+
"-".codePointAt(0);
|
|
237
|
+
"=".codePointAt(0);
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region ../../node_modules/.pnpm/args-tokens@1.0.2/node_modules/args-tokens/lib/resolver-Cx5Pw6-S.js
|
|
240
|
+
/**
|
|
241
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
242
|
+
* @license MIT
|
|
243
|
+
*/
|
|
244
|
+
/**
|
|
245
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
246
|
+
* @license MIT
|
|
247
|
+
*/
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region ../../node_modules/.pnpm/args-tokens@1.0.2/node_modules/args-tokens/lib/index.js
|
|
250
|
+
/**
|
|
251
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
252
|
+
* @license MIT
|
|
253
|
+
*/
|
|
254
|
+
/**
|
|
255
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
256
|
+
* @license MIT
|
|
257
|
+
*/
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region ../gunshi/src/error.ts
|
|
260
|
+
/**
|
|
261
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
262
|
+
* @license MIT
|
|
263
|
+
*/
|
|
264
|
+
const COMMAND_ERROR_BRAND = Symbol.for("gunshi.CommandError");
|
|
265
|
+
/**
|
|
266
|
+
* Error for a failure that a command expects, such as a project that does not exist.
|
|
267
|
+
*
|
|
268
|
+
* When a command throws it from `run`, or a plugin throws it from a command decorator, the global
|
|
269
|
+
* options plugin prints its message to stderr, followed by a `Caused by:` line for each cause,
|
|
270
|
+
* without a stack trace. It sets the exit code of the process to {@link CommandError.exitCode},
|
|
271
|
+
* and `cli()` resolves with `undefined`. `onErrorCommand` is still called with the error, after the
|
|
272
|
+
* exit code is set, so the hook can change it.
|
|
273
|
+
*
|
|
274
|
+
* It is not printed, and `cli()` rejects with it, in these cases:
|
|
275
|
+
*
|
|
276
|
+
* - `usageSilent` is set, for the caller to report it
|
|
277
|
+
* - it is thrown from a hook, while a plugin extends the command context, or while a lazy command
|
|
278
|
+
* loads, outside the command decorators
|
|
279
|
+
* - the CLI runs without the global options plugin, as `gunshi/bone` does
|
|
280
|
+
*
|
|
281
|
+
* Any other error thrown from a command is not printed either, and `cli()` rejects with it.
|
|
282
|
+
*
|
|
283
|
+
* Each package of gunshi (`gunshi`, `@gunshi/plugin`, `@gunshi/definition`) bundles its own copy of
|
|
284
|
+
* this class, so `instanceof` does not match an error from another copy. Use
|
|
285
|
+
* {@link isCommandError} to tell it.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```js
|
|
289
|
+
* import { CommandError, define } from 'gunshi'
|
|
290
|
+
*
|
|
291
|
+
* export default define({
|
|
292
|
+
* name: 'show',
|
|
293
|
+
* args: { project: { type: 'positional', description: 'Project name' } },
|
|
294
|
+
* run: async ctx => {
|
|
295
|
+
* const project = await findProject(ctx.values.project)
|
|
296
|
+
* if (!project) {
|
|
297
|
+
* throw new CommandError(`Project ${ctx.values.project} not found`, { code: 'not_found' })
|
|
298
|
+
* }
|
|
299
|
+
* ctx.log(project.description)
|
|
300
|
+
* }
|
|
301
|
+
* })
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
var CommandError = class extends Error {
|
|
305
|
+
/**
|
|
306
|
+
* The name of the failure, for programs to read. See {@link CommandErrorOptions.code}.
|
|
307
|
+
*/
|
|
308
|
+
code;
|
|
309
|
+
/**
|
|
310
|
+
* The exit code of the process, an integer from 1 to 255.
|
|
311
|
+
*/
|
|
312
|
+
exitCode;
|
|
313
|
+
/**
|
|
314
|
+
* Create an error for a failure that a command expects.
|
|
315
|
+
*
|
|
316
|
+
* @param message - The message printed to the user. It may have more than one line.
|
|
317
|
+
* @param options - The code, the exit code and the cause of the failure
|
|
318
|
+
*/
|
|
319
|
+
constructor(message, options = {}) {
|
|
320
|
+
super(message, { cause: options.cause });
|
|
321
|
+
this.name = "CommandError";
|
|
322
|
+
this.code = options.code;
|
|
323
|
+
this.exitCode = isFailureExitCode(options.exitCode) ? options.exitCode : 1;
|
|
324
|
+
Object.defineProperty(this, COMMAND_ERROR_BRAND, {
|
|
325
|
+
value: true,
|
|
326
|
+
enumerable: false,
|
|
327
|
+
writable: false,
|
|
328
|
+
configurable: false
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* Check whether an error is a {@link CommandError}.
|
|
334
|
+
*
|
|
335
|
+
* An error from another copy of gunshi is recognized through the `Symbol.for('gunshi.CommandError')`
|
|
336
|
+
* brand. It is not recognized by `error.name`, so an error of the app that is also named
|
|
337
|
+
* `CommandError` is not taken for one.
|
|
338
|
+
*
|
|
339
|
+
* @param error - An unknown error
|
|
340
|
+
* @returns `true` if the error is a {@link CommandError}
|
|
341
|
+
*/
|
|
342
|
+
function isCommandError(error) {
|
|
343
|
+
if (error instanceof CommandError) return true;
|
|
344
|
+
if (!isRecord(error) || !Object.hasOwn(error, COMMAND_ERROR_BRAND) || error[COMMAND_ERROR_BRAND] !== true) return false;
|
|
345
|
+
return hasCommandErrorShape(error);
|
|
346
|
+
}
|
|
347
|
+
function isRecord(value) {
|
|
348
|
+
return typeof value === "object" && value !== null;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Check whether `code` is an own or inherited property that is a string or `undefined`.
|
|
352
|
+
*
|
|
353
|
+
* Constructors always assign `code`, even when the option is omitted.
|
|
354
|
+
*
|
|
355
|
+
* @param error - An error from another copy
|
|
356
|
+
* @returns `true` if `code` has the expected type
|
|
357
|
+
*/
|
|
358
|
+
function hasOptionalStringCode(error) {
|
|
359
|
+
return "code" in error && (error.code === void 0 || typeof error.code === "string");
|
|
360
|
+
}
|
|
361
|
+
function hasCommandErrorShape(error) {
|
|
362
|
+
return hasOptionalStringCode(error) && typeof error.message === "string" && isFailureExitCode(error.exitCode);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Check whether a value is an exit code of a failure: an integer from 1 to 255.
|
|
366
|
+
*
|
|
367
|
+
* @param value - The value to check
|
|
368
|
+
* @returns `true` if the value is an exit code of a failure
|
|
369
|
+
*/
|
|
370
|
+
function isFailureExitCode(value) {
|
|
371
|
+
return Number.isInteger(value) && value >= 1 && value <= 255;
|
|
372
|
+
}
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region ../gunshi/src/json.ts
|
|
375
|
+
/**
|
|
376
|
+
* @author kazuya kawaguchi (a.k.a. kazupon)
|
|
377
|
+
* @license MIT
|
|
378
|
+
*/
|
|
379
|
+
/**
|
|
380
|
+
* The argument that a command spreads into its `args` to support `--json`.
|
|
381
|
+
*
|
|
382
|
+
* A command that spreads it returns its result from `run`, as an object keyed by a noun, and prints
|
|
383
|
+
* the output for people with `ctx.log`. With `--json`, the global options plugin silences `ctx.log`,
|
|
384
|
+
* prints the result as one JSON document to stdout, and prints a failure as one line of JSON to
|
|
385
|
+
* stderr. `--json` given to a command that does not spread it stops the command before it runs, with
|
|
386
|
+
* `json_not_supported`.
|
|
387
|
+
*
|
|
388
|
+
* The `json` argument has a mark, by which gunshi tells it from a `json` argument of the command's
|
|
389
|
+
* own, which gunshi does not touch. To give it a short name, spread it:
|
|
390
|
+
* `{ ...jsonArgs.json, short: 'j' }`. A lazy command spreads it into its definition too.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```js
|
|
394
|
+
* import { define, jsonArgs } from 'gunshi'
|
|
395
|
+
*
|
|
396
|
+
* export default define({
|
|
397
|
+
* name: 'list',
|
|
398
|
+
* args: { ...jsonArgs },
|
|
399
|
+
* run: async ctx => {
|
|
400
|
+
* const projects = await fetchProjects()
|
|
401
|
+
* for (const project of projects) {
|
|
402
|
+
* ctx.log(project.name)
|
|
403
|
+
* }
|
|
404
|
+
* return { projects }
|
|
405
|
+
* }
|
|
406
|
+
* })
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
const jsonArgs = { json: {
|
|
410
|
+
type: "boolean",
|
|
411
|
+
description: "Output the result as JSON",
|
|
412
|
+
[JSON_ARG]: true
|
|
413
|
+
} };
|
|
168
414
|
//#endregion
|
|
169
415
|
//#region ../gunshi/src/definition.ts
|
|
170
416
|
/**
|
|
@@ -288,4 +534,4 @@ function lazyWithTypes() {
|
|
|
288
534
|
* @license MIT
|
|
289
535
|
*/
|
|
290
536
|
//#endregion
|
|
291
|
-
export { createCommandContext, define, defineWithTypes, lazy, lazyWithTypes };
|
|
537
|
+
export { CommandError, createCommandContext, define, defineWithTypes, isCommandError, jsonArgs, lazy, lazyWithTypes };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gunshi/definition",
|
|
3
3
|
"description": "utilities for gunshi command definition",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.6",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "kazuya kawaguchi",
|
|
7
7
|
"email": "kawakazu80@gmail.com"
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"gunshi": "1.0.0-rc.
|
|
55
|
+
"gunshi": "1.0.0-rc.6",
|
|
56
56
|
"publint": "^0.3.20",
|
|
57
57
|
"tsdown": "0.22.14"
|
|
58
58
|
},
|