Comments (14)
The git stash
example might be done in Commander as nested commands. Making it look similar:
import { Command } from 'commander';
const program = new Command();
program.configureHelp({ subcommandTerm: (cmd) => cmd.name() + ' ' + cmd.usage() });
const stashCommand = program.command('stash');
stashCommand.command('list').usage('[<log-options>]')
stashCommand.command('show').usage('[-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]');
stashCommand.command('drop').usage('[-q | --quiet] [<stash>]');
program.parse();
% node git.mjs help stash
Usage: git stash [options] [command]
Options:
-h, --help display help for command
Commands:
list [<log-options>]
show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]
drop [-q | --quiet] [<stash>]
help [command] display help for command
from commander.js.
This has not had any likes (yet) and neither did #80, but I am impressed by the examples here. Keeping this open for more consideration.
from commander.js.
I appreciate the examples and finding previous issue, thanks.
Having more than one usage string or usage example in the synopsis section is a pattern that Commander does not directly support.
from commander.js.
The only workaround is to include a newline in the usage string, but successive usages are not correctly indented.
This work-around seems to work reasonably, with manual indentation?
import { Command } from 'commander';
const program = new Command();
program.usage(`[options] [ script.js ] [arguments]
node inspect [options] [ script.js | host:port ] [arguments]`);
program.parse();
% node node.mjs --help
Usage: node [options] [ script.js ] [arguments]
node inspect [options] [ script.js | host:port ] [arguments]
Options:
-h, --help display help for command
from commander.js.
This work-around seems to work reasonably, with manual indentation?
Hardcoding a 7 char indent is exactly the workaround I'm using. Sorry I didn't mention that. Smells a wee bit bad, but not a big deal ;)
In my particular use case, nested commands won't work, and in fact Option.conflicts
isn't applicable either, as the command has different semantics (and options) depending on whether the first positional arg is a file or directory, similar to cp
which I'll add as an example above.
from commander.js.
Also, is your name an Anglicization of the French so that people pronounce it correctly? I had a friend with that name and hated how it was mispronounced. Feel free to delete this comment! :)
from commander.js.
Gee? Scottish origins in my family tree.
from commander.js.
Another possible approach is to include text in the long description, which is fairly freeform (and avoid the 7 char indent annoyance):
program.command('stash')
.description(`Synopsis:
git stash list [<log-options>]
git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]
git stash drop [-q | --quiet] [<stash>]`)
.summary('Stash the changes in a dirty working directory away');
% node description.mjs stash -h
Usage: description stash [options]
Synopsis:
git stash list [<log-options>]
git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]
git stash drop [-q | --quiet] [<stash>]
Options:
-h, --help display help for command
from commander.js.
Also, is your name an Anglicization of the French so that people pronounce it correctly? I had a friend with that name and hated how it was mispronounced. Feel free to delete this comment! :)
Gee? Scottish origins in my family tree.
So the French name, Jean-Guy, is pronounced like John Ghee. Sadly, most American colleagues of the aforementioned friend pronounced it as the English words, "gene guy".
from commander.js.
As I hinted before, this isn't a big deal. The workaround is fine.
That said, since this is JS, I think it would be easy enough to support usage
accepting a string array and acting accordingly.
Alternatively, allow multiple calls to usage
. Or both, as is the case with conflicts
.
If you agree with one of the above, I could look into submitting a PR.
from commander.js.
I thought about this a little trying to work around it for one of my CLIs. It's implicit for all the CLIs that have multiple usages that aren't keyed by subcommands like git stash
that there is some way to distinguish which of the usages is in effect.
Looking at git rebase
for example:
git rebase [-i | --interactive] [<options>] [--exec <cmd>]
[--onto <newbase> | --keep-base] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
--continue
,--skip
, etc are unique to usage 3.-i
narrows it to usage 1 or 2.--keep-base
narrows it to usage 1.--root
narrows it to usage 2.- two non-option arguments narrow it to usage 1.
So one way to implement this:
- Start with a list of all usages as candidates.
- For each parsed option narrow the candidate list.
- If an option isn't valid for any of the remaining candidates, see if it is valid for any of the eliminated usages.
- If it is, then report it as a conflict
- If not, report it as an unrecognized option.
In addition, it would be good to have a way to narrow the candidate list by the type of non-option argument. For example, cp
as shown earlier has usages based on whether an argument is a file or directory. My personal use case has this as well. So perhaps a way to process non-option arguments (aka "remaining arguments"). For example:
program.
.argument('<mode>', 'mode to use')
.argument('<file>', 'string to split', validateCallback)
where the optional validateCallback
returns true only if the argument matches whatever is expected of that argument, e.g. in this case that it exists in the file system and that it is a file and not a directory. For single usage cases, this adds a feature that currently does not exist: A way to validate non-option arguments (If you like this idea separate from supporting multiple usages, let me know and I can create a separate feature request). For multi-usage cases, it is also used to narrow the candidate list.
If you find this interesting, happy to iterate an API design with you if that helps.
from commander.js.
Note that custom processing of arguments is supported. See: https://github.com/tj/commander.js#custom-argument-processing
Like:
.argument('<first>', 'integer argument', myParseInt)
from commander.js.
Validating the command-line arguments using the usage (strings or otherwise) is an interesting idea, but I think going to be too much work. Your examples and comments show the complicated ways utilities overload their usage, like cp
file/directory argument type, or rebase
with 7 modes based on the exclusive options.
Simply supporting calling usage multiple times should be technically easy, but the earlier issue got no upvotes in 7 years. Since there are easy work-arounds, I don't think it is worth adding without some upvotes or interest on this issue. (Which is why I haven't closed it yet, giving it a chance to gather some support.)
from commander.js.
I thought about this a little trying to work around it for one of my CLIs.
And to be clear, thanks for sharing. Both for explaining some of your own use-case, and the thoughts on possible approaches.
from commander.js.
Related Issues (20)
- Missing `this` type for `action` HOT 5
- Hey ! My project got crashed.. please check description for more detail HOT 2
- The root `--help` doesn't respect `.usage()` in a command HOT 2
- [Bug] Unexpected arguments ignored HOT 2
- Commander v12 breaks implicit negative flag. HOT 1
- Dev: purging stale release branches HOT 1
- 115 HOT 1
- Add support for option to output object HOT 8
- Handle the case when node called with the "-e" argument HOT 4
- Compatible with deno HOT 2
- Neutral JavaScript runtime support (Deno, Bun, etc) HOT 11
- Allow option to conflict with the negation of another option. HOT 5
- When use .command("dtsgen <files>")๏ผthis params files in macOS and Windows is errors caused by differences HOT 3
- Requiring via `<` does not work. HOT 4
- how to use the ESM module to import and export ? HOT 2
- Option.fullDescription() does not exist HOT 2
- Is it possible for an option to conflict with an argument? HOT 5
- Usage with Bun HOT 5
- Action not exiting when using nodejs streams HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from commander.js.