Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent options with flags already in use from being added #1923

Closed

Conversation

aweebit
Copy link
Contributor

@aweebit aweebit commented Jul 31, 2023

Pull Request

Problem

Adding options with flags that are already in use is allowed.

Demo

const { Command } = require('commander');

var program = new Command();
program
  .option('-a, --first')
  .option('-a, --second');
program.parse(['-a'], { from: 'user' });
console.log(program.opts()); // { first: true }

var program = new Command();
program
  .option('-V, --version [arg]', 'added via .option()')
  .version('1.0.0')
  .action(() => {
    console.log('called action handler');
    console.log('version value is', program.opts().version);
  });
program.outputHelp(); // both version option and option added via .option() are shown
program.parse(['-V', '2.0.0'], { from: 'user' }); // action handler not called

ChangeLog

Changed

  • Breaking: throw an error when .addOption() is called with an option whose flags are already in use

Peer PRs

…solving similar problems

@shadowspawn
Copy link
Collaborator

Commander only checks some of the possible mistakes/accidental-misuse by the "author", but willing to consider more as they come up.

Related: #1903

@shadowspawn
Copy link
Collaborator

I am hopeful this can be done with less code by using the internal Command._findOption.

@shadowspawn

This comment was marked as resolved.

@shadowspawn shadowspawn added the semver: major Releasing requires a major version bump, not backwards compatible label Aug 1, 2023
@aweebit
Copy link
Contributor Author

aweebit commented Aug 1, 2023

I am hopeful this can be done with less code by using the internal Command._findOption.

Done in 5ec2249. Further simplifications would make the error message less detailed.

@aweebit

This comment was marked as resolved.

@shadowspawn

This comment was marked as resolved.

@shadowspawn
Copy link
Collaborator

The way an option is usually referred to in the end-user messages is '${option.flags}' and we can use same format in author facing error. My reasoning for this format is the combo is potentially more recognisable than a single flag.

I suggest listing the found item in same format too. I am deliberately simplifying here and not doing the error breakdown you did (although I admire the effort). I think it will be clear enough to the author?

    const matchingOption = (option.short && this._findOption(option.short)) || (option.long && this._findOption(option.long));
    if (matchingOption) {
      throw new Error(`Cannot add option '${option.flags}' as an option using same flag has already been been added
- conflicts with option '${matchingOption.flags}'`);
    }

@aweebit
Copy link
Contributor Author

aweebit commented Aug 1, 2023

The way an option is usually referred to in the end-user messages is '${option.flags}' and we can use same format in author facing error. My reasoning for this format is the combo is potentially more recognisable than a single flag.

A combo is already used, but with option-argument and extra spaces removed: see flagString on line 510. I think it is better than what you suggest because the actual flags are the only thing that matters here.

@shadowspawn
Copy link
Collaborator

I did forget you were using one combo! But no need to calculate it, just use '${option.flags}' which matches what the author specified and other messages.

I think identifying the conflicting option is important and the whole matchingOption.flags is easier to find than a flag alone.

Co-authored-by: John Gee <john@ruru.gen.nz>
@aweebit
Copy link
Contributor Author

aweebit commented Aug 1, 2023

Fair enough. I used your suggestion in 9721ae8, just added the command name and changed "as" to "since" because it reads better (with "as", I first read it as (Cannot add option '${option.flags}' as an option using same flag) has already been added and was confused).

const matchingOption = (option.short && this._findOption(option.short))
|| (option.long && this._findOption(option.long));
if (matchingOption) {
throw new Error(`Cannot add option '${option.flags}'${this._name && ` to '${this._name}'`} since an option using same flag has already been added
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

command name is good addition 👍

@shadowspawn shadowspawn changed the base branch from develop to release/12.x August 1, 2023 08:46
Copy link
Collaborator

@shadowspawn shadowspawn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good to me, simple and informative and effective.

Just needs a couple of tests, for short and long conflicts.

aweebit added a commit to aweebit/commander.js that referenced this pull request Aug 3, 2023
_registerOption() has been borrowed from deb3bdf in tj#1923.
Watch out for merge conflicts!
@shadowspawn
Copy link
Collaborator

(I am concerned this may be a blocking change for people upgrading big programs from older versions of Commander, because their code will be broken until they resolve the clashes. I might want to add configuration to make this optional specifically to ease migration, but won't do that now. We can do a prerelease and see if people hit issues that they considered an obstacle.)

aweebit added a commit to aweebit/commander.js that referenced this pull request Aug 8, 2023
- Check if new option is usable with current settings before registering
- Check if registered options remain usable when updating settings

_registerOption() has been borrowed from deb3bdf in tj#1923.
@shadowspawn shadowspawn added the on hold Not a current focus due to dependencies on other PR or number of other PR label Aug 8, 2023
@shadowspawn shadowspawn removed the on hold Not a current focus due to dependencies on other PR or number of other PR label Sep 4, 2023
@shadowspawn
Copy link
Collaborator

Carried on in #2055

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver: major Releasing requires a major version bump, not backwards compatible
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants