Skip to content

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

Closed
aweebit wants to merge 7 commits intotj:release/12.xfrom
aweebit:feature/option-flag-conflicts
Closed

Prevent options with flags already in use from being added#1923
aweebit wants to merge 7 commits intotj:release/12.xfrom
aweebit:feature/option-flag-conflicts

Conversation

@aweebit
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy link
Copy Markdown
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.

2 participants