-
Notifications
You must be signed in to change notification settings - Fork 10
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
Restructure result conventions (again) #70
Comments
This design seems quite reasonable to me, with a nit that I don't love the variable name
|
+1 for Agreed, this design seems reasonable to me as well. However, while were discussing the results object, I'd like to challenge the upcoming relevance of the name What are folks thoughts around this convention? const { options } = parseArgs({
options: {
foo: {
type: 'string'
},
},
}) |
I like
A wild challenge would be to drop |
One complication with using
(I don't see any issues against Meow though, so perhaps does not come up much in practice.) |
I've definitely run into that kind of issue before, but i think "options" is a pretty confusing name to pass as a property here. |
Nit. I have gone off Line 31 in b412095
I wasn't enthusiastic about my original suggestion of |
Can you point out the extra code? I'm struggling to see complications in the example.
I'm not sure this should influence API naming conventions. We could simply rename the test vars from |
I wasn't very clear, or accurate... What I was thinking was if there was already a variable called
to
|
Although might be a good time to check expectations! In the future, if we added
|
If we ever add defaults, it absolutely must be possible to determine if a defaulted value was explicitly passed or not; to do this in yargs I have to default things to, eg, "parsed" is better than "passed", because "passed" implies it's unchanged from what the user passed - but it is changed, because it's parsed. |
For interest, Commander only added support for determining source of option value in v8.3.0 in late 2021 after about a decade without a mechanism. e.g. |
@shadowspawn @ljharb what if rather than an object that contains all objects that were explicitly set, we have the opposite I think this could serve a similar purpose we're looking to solve, but to me makes the return values less confusing. We could then perhaps run with:
I could also be pushed towards:
But I don't love the overloaded term |
How do we feel about just having one object for parsed options containing the value and usage metadata? I think all the discussion points in issue #43 and PR #63 for input options apply to the outputs e.g. More natural to have all the information in one place and lays the foundation for future iterations: // node parsedOptions.js --foo bar
const optionConfigs = {
foo: { type: 'string' },
baz: { type: 'string', default: 'hello' },
}
const { options, positionals } = parseArgs({ options: optionConfigs })
/*
options === {
foo: {
value: 'bar',
defaulted: false,
parsed: true,
},
baz: {
value: 'hello',
defaulted: true,
parsed: false,
},
}
*/ |
I like #70 (comment), assuming (altho when would |
I could definitely see this being helpful. I think it addresses the same concerns and establishes a pattern for future iterations.
Good point 👍 I didn't fully reason about that and was focused on covering the edge cases discussed so far (e.g. identifying when options are passed and/or defaulted). So we can probably nix the |
Here's a scrappy example of the type ParsedOptions = {
[option: string]: {
/** Value corresponding to the `optionConfig.type`; Set to `optionConfig.default` if option was not passed */
value: string | boolean
/** True if the parsed option's value used the `optionConfig.default` */
defaulted: boolean;
// Future iterations...
/**
* Could be helpful to understand how the parser inferred `strict:false` options: e.g.
* node inferred.js --foo // parsedOptions === { foo: { type: 'boolean', value: true }}
*/
type: 'string' | 'boolean'
/** Number of times the option was defined */
count: number
}
} As an example of how this pattern can be leveraged, a // node verbose.js -vvv
const { options } = parseArgs()
/*
options === {
v: {
type: 'boolean', // Helpful to see what the parser inferred
value: true, // Returns a single option for type:'boolean'
count: 3, // Metadata includes the frequency of the option
}
}
*/ |
Actually, I would rather delete it. (Which I'll suggest in next comment!) The sense of "defaulted" is referencing a feature we don't even have yet. |
Short version: I propose we drop Long version I already speculated about dropping it earlier in this issue, and got no defenders (#70 (comment)). I think it is time to get serious about deleting it! I have been asking what it is for since Nov last year and got no compelling answers, and we are still trying to give it a sensible name, with no compelling use case. 😱 Why have the field at all? Suggested: differentiate between the case where an option was passed no argument, vs., an option not being set at all (#13 (comment)) Suggested: simplify testing for existence of option in args (#24 (comment)). Suggested: tell where value came from in future (e.g. if add defaults) |
My initial reaction is no, I think combining the values and usage metadata is overkill and complicating the primary target audience. i.e.
In the future, if there is demand for this as a base for building richer parsers and custom behaviour, I think there could be a metadata containing property. (Which could perhaps include the value as well so was self-contained.) |
This feels similar to the initial reactions when I proposed the restructured option configs API. I'm trying to imagine what Here is an example of what I'm trying to avoid after say one year of features and enhancements: // Where we're headed...
const {
foundOptions: { foo: true },
values: { foo: true },
defaulted: { foo: false },
count: { foo: 2 },
types: { foo: 'boolean' },
etc...
} = parseArgs() // Proposal...
const {
options: {
foo: {
type: 'boolean',
value: true,
defaulted: false,
count: 2,
etc...
},
},
} = parseArgs() As far as the proposal being complicated for the target audience, I'm not sure I follow. I believe my suggestion improves the robustness of the API and is actually more intuitive for the target audience. |
Certainly if we have no "defaults" at all, then it's easy to see when an option isn't provided. However, it seems pretty important for almost every CLI workflow i know to have defaults, and for some, it's critical to be able to know whether they're defaulted or not. |
Fair concern. I don't see organic growth of user-level properties as a likely outcome. Other libraries have had a decade of features and enhancements, and have a single layer of object values available. See #12 (comment) If we add more information for people building richer parsers, I do prefer the idea of putting that richer metadata into one property and not scattering across multiple top level properties. Other metadata mentioned in #52 (comment) #52 (comment) #84 |
(More metadata in: #84) I am thinking a property like e.g.
|
100%, Totally valid argument. However, I still (personally) feel the combination of option value and metadata is the more intuitive/future-facing API. The closest example I can think of is RegExp match objects. Notice: Each match object contains the full match, number and named capture group values, the start index, indices of capture groups, etc. Now imagine if instead I may be reaching a bit with the above demo, but am attempting to provide a real-world example of a parsed input that includes values and metadata in the same result to demonstrate the intuitiveness of the API. |
I think the overwhelmingly common case is going to be just getting the value without any metadata, possibly with destructuring, as in let { values } = parseArgs(opts);
let { foo, bar = defaultBar } = values; And that gets much more annoying if metadata is mixed with values. I think it's important to optimize for the common case. Note that even in the |
(Exploring interest, not pushing for a change!) I do like
I am not keen on using same property on input and output despite the symmetry (#70 (comment)), and feel there is additional confusion on the input usage with common usage of "options" for an option bag. Perhaps the input property could be renamed? Inspired in part by
|
No objections from me. I was thinking the same thing after the initial concerns around using const { options, positionals } = parseArgs({
strict: false,
optionsConfig: { host: { type: 'string' }}
}); |
Landed #83. Closing this, the culmination for five months of issues. 😅 |
A new attempt to improve the results (#12 #22 #23 #38 #55) prompted by proposed changes to input configuration (#63), and with better understanding of some of original intent (#38 (comment)).
For context, the configuration syntax proposed in #63 is:
Result Properties
I propose result has properties for:
optionFound
: object with properties andtrue
value for all parsed options found inargs
.optionFound: { debug: true, name: true, mult: true }
values
: object with properties and values for the parsed optionsvalues: { debug: true, name: 'John', mult: ['a', 'b'] }
positionals
: array of positionals fromargs
positionals: ['alpha']
This is a rename of
flags
tooptionFound
. The intent offlags
name was the options without values, but the proposed meaning is all options found inargs
as per original proposal.vs status quo: storing all the options found in
optionFound
matches the current implementation, but not the current README examples which are only showingtrue
for boolean options. See also following section on values.The
optionFound
field is initially redundant and can be calculated fromvalues
. However, it offers a semantic affordance now, and is not redundant if we add support for defaults (or setting known boolean flags to false, say) . Both Command and Yargs did not start out with a concept of whether the option was found in the inputargs
, and that led to requests later as not possible to tell from a value whether was an explicit default (say for a string) or an implicit default (say for a boolean) or for from the parsed arguments.Values
I propose
values
property holds for an option:args
(normal case for option withtype:'string'
)true
for option used as boolean flag (normal case for option withtype:'boolean'
)multiples:true
, withtrue
and/or string elementsThe change is storing
true
as the value for a found boolean option. I think this is the natural value, and is what other parsing libraries do such as Command and Yargs.vs status quo: the current README examples are omitting boolean options entirely. The current implementation is storing
undefined
rather thantrue
.The text was updated successfully, but these errors were encountered: