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

feat: rethink how options are inherited by commands #766

Merged
merged 13 commits into from Jan 29, 2017
Merged

feat: rethink how options are inherited by commands #766

merged 13 commits into from Jan 29, 2017

Conversation

bcoe
Copy link
Member

@bcoe bcoe commented Jan 22, 2017

BREAKING CHANGE: by default options, and many of yargs' parsing helpers will now default to being applied globally; such that they are no-longer reset before being passed into commands.

This is to address the fact that the existing behavior has created a lot of confusion: #659, #762, #764, etc.

This is going to be a pretty major change, and would appreciate as much feedback as possible as it's built out.

@nexdrew I'm going to merge your work from #734 into this.

@bcoe
Copy link
Member Author

bcoe commented Jan 22, 2017

@nexdrew, @JaKXz, @maxrimue, @lrlna this is getting much closer to being finished, and I would love some feedback.

I've combined a lot of yargs' configuration logic into populateParserHintObject, populateParserHintArray; This both cuts down on repeated code, and provides a single location to enable options/settings globally.

Along the way I've been cleaning up some API inconsistencies, and correcting a few bugs. For starters, it would be wonderful to have folks run this branch against some of their projects, specifically projects that use commands heavily (CC: @nexdrew I know you have a few).

@bcoe bcoe changed the title [WIP] feat: rethink how options are inherited by commands feat: rethink how options are inherited by commands Jan 23, 2017

// we apply validation post-hoc, to so that custom
// checks get passed populated positional arguments.
yargs._runValidation(innerArgv, aliases)
Copy link
Member Author

Choose a reason for hiding this comment

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

the little bit of refactoring in this method was to hold off on applying validation until after we've populated variadic values -- this was largely so that one can provide a .check() at the top level, and have it passed the appropriate post-processed object after commands are parsed.

}
}

return setPlaceholderKeys(argv)
}

self._runValidation = function (argv, aliases) {
Copy link
Member Author

Choose a reason for hiding this comment

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

_runValidation was pulled into a helper, to make it easier to run optionally -- this was so that we could hold off on running it until the last moment in the case of commands.

Copy link
Member

@maxrimue maxrimue left a comment

Choose a reason for hiding this comment

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

This looks really, really awesome! Found nothing concerning and love the changes.

@@ -1112,9 +1107,9 @@ Options:
Missing required arguments: run, path
```

<a name="demandCommand"></a>.demandCommand(min, [minMsg])
<a name="demandCommand"></a>.demandCommand([min=1], [minMsg])
Copy link
Member

Choose a reason for hiding this comment

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

Very awesome, I love this one

if (!yargs._hasOutput()) populatePositionals(commandHandler, innerArgv, currentContext, yargs)

if (commandHandler.handler && !yargs._hasOutput()) {
commandHandler.handler(innerArgv)
}

// we apply validation post-hoc, to so that custom
Copy link
Member

Choose a reason for hiding this comment

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

tiny typo here

Copy link
Member

@JaKXz JaKXz left a comment

Choose a reason for hiding this comment

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

I think this looks great at a glance/distance/overall, and I'm starting to understand the reason for the change. 👍

@@ -418,6 +418,9 @@ Check that certain conditions are met in the provided arguments.
If `fn` throws or returns a non-truthy value, show the thrown error, usage information, and
exit.

`global` indicates whether or not `check()` should be enabled both
Copy link
Member

Choose a reason for hiding this comment

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

nit: I think

indicates whether check() should be enabled...

reads a little clearer, since I don't have to think about negations and my pyrate brain don't hurt.

function populateParserHintArray (type, keys, value) {
keys = [].concat(keys)
keys.forEach(function (key) {
self.global(key)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of explicitly marking everything as global, I kinda figured it would be easier to just invert the reset logic to assume that everything is global unless explicitly marked as non-global.

In that approach, we could perhaps change options.global to be options.local, treat yargs.global(opts, false) as a synonym for a new API method yargs.local(opts), and only worry about resetting local options in the reset method. (I think positional args for a command would be the only options to default as local.)

Thoughts? Is my view on the matter overly simplistic?

Copy link
Member

@nexdrew nexdrew left a comment

Choose a reason for hiding this comment

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

Besides questioning the general approach 😄, nothing too major; see below.

@@ -596,6 +585,8 @@ function Yargs (processArgs, cwd, parentRequire) {
self.count(key)
}

self.global(key, opt.global !== false)
Copy link
Member

@nexdrew nexdrew Jan 24, 2017

Choose a reason for hiding this comment

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

1/2 If we stick with this approach, this needs to be the very last statement in the else block for this method; otherwise, whatever gets checked and called after it will make the option global again.

})
} else {
// a single key value pair 'x', parse() {}
self.global(key)
Copy link
Member

@nexdrew nexdrew Jan 24, 2017

Choose a reason for hiding this comment

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

2/2 Similarly, this approach means that you must call .global(opts, false) last to disable an option from being global.

E.g. code like this will not have the desired effect because the call to .coerce() makes the option global again:

require('yargs')
  .describe('opt', 'My local option')
  .string('one')
  .global('opt', false)
  .coerce('opt', opt => 'local_' + opt)
  .argv

This is probably not a big deal, but we should either mention this caveat in the README/docs or go with another approach that doesn't have this caveat.

@bcoe bcoe merged commit 35218a8 into 7.x Jan 29, 2017
bcoe added a commit that referenced this pull request Feb 18, 2017
BREAKING CHANGE: by default options, and many of yargs' parsing helpers will now default to being applied globally; such that they are no-longer reset before being passed into commands.
bcoe added a commit that referenced this pull request Feb 26, 2017
BREAKING CHANGE: by default options, and many of yargs' parsing helpers will now default to being applied globally; such that they are no-longer reset before being passed into commands.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants