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: configuration setting for running detailed parse #1472

Closed
wants to merge 7 commits into from

Conversation

bcoe
Copy link
Member

@bcoe bcoe commented Nov 5, 2019

You can now set yargs.parserConfiguration({detailed: true}) and receive detailed results from yargs-parser.

This provides an interface into @juergba's defaulted functionality.

fixes #1334

CC: @coreyfarrell.

@bcoe bcoe requested a review from mleguen November 5, 2019 05:58
Copy link
Member

@mleguen mleguen left a comment

Choose a reason for hiding this comment

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

The feature itself looks interesting, but I'm uneasy about the quantity of new ifs this feature brings, and the increased complexity of command.js and yargs.js afterwards.

In particular, this is hard to believe, when reading tests, that so much different preprocessing before calling command handlers on the first hand, and before returning from yargs.parse() on the other hand, leads to the same results... I hope we will never have issues there :-)

This reminds me of the debate about having a parallel detailed argv structure a few months ago, which we never did because we were afraid of adding too much complexity...

if (!detailedPositionalParse[key]) {
detailedPositionalParse[key] = {
value: parsed.argv[key],
defaulted: !!parsed.defaulted[key]
Copy link
Member

Choose a reason for hiding this comment

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

If detailerPositionalParse's list of fields is intended to grow, we should not have to add these fields in 2 places.

I recommand refactoring this part.


// short-circuit parse.
if (!unparsed.length) return
if (!unparsed.length) return detailedPositionalParse
Copy link
Member

Choose a reason for hiding this comment

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

In that case, postProcessPositionals (now runParserOnPositionals) used to return nothing and populatePositionals (now addPositionalsToArgv) used to return positionalMap.

Now, runParserOnPositionals returns {} and addPositionalsToArgv returns it too. Isn't something missing? Shouldn't detailedPositionalParse contain at least values from positionalMap before being returned?

@bcoe
Copy link
Member Author

bcoe commented Nov 6, 2019

@mleguen I agree about concerns of adding complexity, however I also feel we need some way to expose a detailed parse from yargs-parser (which is an advanced feature @coreyfarrell asked for for nyc, and other folks have asked for multiple times throughout the years). If we're going to add this feature, I think we need it to work in .parse(), command(), and all other locations where a parse occurs.

tldr; I think this ultimately is actually a good replacement to yargs.parsed, which only worked in some contexts ... open to other recommendations.

Copy link
Contributor

@coreyfarrell coreyfarrell left a comment

Choose a reason for hiding this comment

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

For now I've reviewed the tests only, I'll try to take another look at the actual code when I have a little more mental bandwidth.

Comment on lines +949 to +953
}, [(argv) => {
return {
hello: 'world'
}
}])
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this final callback argument? Seems like this test could do without it - also with detailed: true I'd expect this callback to use ({argv}) argument (parse.argv instead of just argv).

Copy link
Member Author

Choose a reason for hiding this comment

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

this is middleware, which can be passed as an array as the final argument to a command. The idea with middleware is that it might be a third-party plugin that someone has written, so I think the API surface should continue getting argv (otherwise someone writing middleware would need to detect a detailed parse, vs., a regular parse).

parse.argv.identifier.should.equal('abc123')
parse.argv.foo.should.equal('bar')
parse.argv.banana.should.equal('yellow')
parse.argv.hello = 'world'
Copy link
Contributor

Choose a reason for hiding this comment

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

Setting parse.argv.hello seems unneeded?

.option('banana', {
default: 'yellow'
})
.parse('foo abc123 --apple red')
Copy link
Contributor

Choose a reason for hiding this comment

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

As with the yargs-parser tests it would be good if we could use --apple green to show that yargs.defaulted.apple is cleared when the default value is explicitly set to the default value by the user. Also missing a test above to verify parse.defaulted.apple is unset.

default: 'blerg'
})
.positional('foo', {
default: 'bar'
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the expected behavior when a default is not configured? If you remove this default: 'bar' would this mean parse.defaulted.foo == true && parse.argv.foo === undefined?

parse.argv.id.should.equal('abc123')
parse.argv.identifier.should.equal('abc123')
parse.argv.foo.should.equal('bar')
parse.argv.banana.should.equal('yellow')
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to check argv.apple and defaulted.apple.

@bcoe
Copy link
Member Author

bcoe commented Nov 9, 2019

@coreyfarrell is there a more minimal API surface that would work for your use-case, maybe we could just get away with introducing .parseDetailed and ignoring the .command use case until someone asks for it.

@coreyfarrell
Copy link
Contributor

ignoring the .command use case until someone asks for it.

For my use case const {argv, defaults} = yargs.parse(argsArray) would be adequate, though ignoring the .command use case for now would prevent adding it without a breaking change. Right now setting detailed: true has no meaning anywhere, so using it to change the API of both yargs.parse and .command handlers is not a breaking change.

@bcoe
Copy link
Member Author

bcoe commented Nov 9, 2019

we have this ancient pull request as well, asking us to expose defaults:

#276

I don't love the idea of adding a yargs.defaults() because it feels a bit clunky, why no yargs.aliases(), yargs.etc ... perhaps a compromise would be a method like:

yargs.isDefaulted(string) // returns true or false depending on whether the optoin was defaulted.

☝️ I could imagine adding a handful of options like this.

@bcoe
Copy link
Member Author

bcoe commented Nov 9, 2019

somewhat similar: #294, a way to filter out any keys that have aliases.

@coreyfarrell
Copy link
Contributor

The API of the yargs object is difficult as many of the functions mean "add this thing" rather than "get information about this thing". So I think yargs.isDefaulted(string) makes sense. FWIW my previous message mentioned const {argv, defaults} = yargs.parse(argsArray), this is incorrect. It should have said const {argv, defaulted} = yargs.parse(argsArray). So I think yargs.defaulted avoids the confusion that yargs.defaults would create, though I say that from the prospective of a native English speaker. I'm not sure if defaulted would be so clear to people who are not native English speakers so maybe isDefaulted(string) would be better? If we go in that direction then should we consistently expose isDefaulted(string) instead of defaulted?

@juergba
Copy link
Contributor

juergba commented Nov 10, 2019

What about the error object, which is populated by yargs-parser?
Does it really throw in Yargs? Otherwise it should also be accessible in Yargs to make sense.
In yargs-parser it's populated only and does not throw.

@mleguen
Copy link
Member

mleguen commented Nov 13, 2019

@coreyfarrell As a non-native English speaker, I am used to seeing (and writing) isXxxed functions for this kind of check. So I would recommend using isDefaulted. But that is only ONE non-native english speaker opinion...

@bcoe
Copy link
Member Author

bcoe commented Nov 13, 2019

@mleguen @coreyfarrell I'm leaning towards the isDefaulted, vs., the current state of this branch.

@bcoe bcoe added the blocked label Nov 16, 2019
@bcoe
Copy link
Member Author

bcoe commented Nov 16, 2019

I need to actually sit down and work on the refactor discussed here, i.e., adding a helper method rather than a sweeping change to how parsing works based on a configuration setting.

@bcoe bcoe closed this Feb 9, 2020
@bcoe
Copy link
Member Author

bcoe commented Feb 9, 2020

let's revisit this when someone has time, it would be good to expose the defaulted behavior, but I'm not 100% sure of the best implementation just yet.

@bcoe
Copy link
Member Author

bcoe commented Feb 9, 2020

I think @coreyfarrell suggested something along the lines of a symbol on the argv object, which could be used by yargs.isDefaulted(argv, key) to check whether a given field was defaulted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat: track which arguments have been defaulted
4 participants