GithubHelp home page GithubHelp logo

Comments (16)

pacocoursey avatar pacocoursey commented on May 3, 2024 2

I have completed this, but I could use a few more use-cases to ensure it serves the correct purpose.

Using your example above, should ./my-program -vvvv -v output '-v': 5?

What if the flag '-vvv' exists? Should the following return case 1 or case 2?

// ./my-program -vvv -vvv

const args = arg({
    '-vvv': Boolean,
    '-v': [Boolean]
});

console.log(args);

/*
    {
        _: [],

        // case 1...
        '-vvv': true,
        '-v': 3

        // or case 2...
        '-vvv': true
    }
*/

Don't want to miss an edge case 😅

from arg.

blakeembrey avatar blakeembrey commented on May 3, 2024 1

@Qix- Is there a common use-case where making a special case for this is worth it? It'd make it a bit confusing for anyone briefly reading the docs and it's not that hard to do args.x.filter(x => x).length (though obviously more verbose).

In any case, shouldn't type() always be called? I was just reading the code and I'd expect something like -x false to work (in case the default is true in code).

from arg.

pacocoursey avatar pacocoursey commented on May 3, 2024 1

Agreed that returning a number for [Boolean] flags seems strange. Seems like the natural solution is to return an array of booleans [true, true, true, ...] and let the user call .length on it themselves.

The core of the issue still exists though: repeating single-hyphen flags should push additional true values onto that array.

from arg.

Qix- avatar Qix- commented on May 3, 2024

Using your example above, should ./my-program -vvvv -v output '-v': 5?

Yes. :)

What if the flag '-vvv' exists?

It won't; it's one of arg's few opinions. Single-dash options are only single letters - we have #4 open to enforce this as the example snippet you provided should throw an error.

from arg.

Qix- avatar Qix- commented on May 3, 2024

@blakeembrey Boolean is already a special case in arg. -x false and -x true are not common GNU argument parsing patterns for booleans. In those cases, false and true would become positional arguments (assuming -x is a boolean type).

That also means that if we kept the current functionality, you'll always end up with an array of true's, which doesn't really add much information aside from using .length anyway.

Further, it makes parsing for condensed arguments (-abcd instead of -a -b -c -d) a bit less intuitive if you accept boolean strings.


The only two edge cases are then:

  • Boolean - if the flag is present, then true; otherwise, false
  • [Boolean] - always a Number, always present and 0 < n, that counts the number of occurrences of the flag in the command line.

Otherwise, all types are as you would expect.

from arg.

blakeembrey avatar blakeembrey commented on May 3, 2024

@Qix- I see. Thoughts on using an internal boolean symbol instead (e.g. export const POSITIONAL = Symbol('positional') (or even '__POSITIONAL__' for older systems)? I'm just thinking about how this from documentation in the README and TypeScript. In terms of TypeScript, it'd require an overload in either case to override Boolean behaviour when the numeric is introduced so feel free to ping me then.

If you use an internal symbol, would it make sense to just always be a number? I don't think I'd really care about true or false if it can never be false anyway.

from arg.

Qix- avatar Qix- commented on May 3, 2024

I'm not sure what problem the boolean symbol would solve. Could you explain a bit more?

As well, perhaps I'm missing something, but [Boolean] argument types are always numbers.

from arg.

blakeembrey avatar blakeembrey commented on May 3, 2024

@Qix- Just API consistency. Right now things are super easy to understand, and there's no documentation on Boolean really needed (even though it is special). After this, in either case, you need to write documentation on the new special behavior. I just thought it was more consistent to introduce a new behavior instead of a change to special case to existing statements. E.g. this statement is false now:

This means the built-in String, Number, and Boolean type constructors "just work" as type functions.

Whereas just adding a new sentence mentioning positional flags is easy:

arg({
  '-x': arg.POS,
  '-y': [arg.POS]
})

To be honest, I'm don't feel too strongly either way. I just get a little worried when I see implicit over explicit behavior of inputs since it requires people to "know" this is special and changes behavior. Imagine coming onto a new project and always explaining that [Boolean] actually produces a number. But, you know, everything else is true to the statement above.

from arg.

rauchg avatar rauchg commented on May 3, 2024

I agree with @blakeembrey – I'd argue it's elegant to maintain type affinity. The number result is very surprising when the "type definition" is [Boolean]

from arg.

Qix- avatar Qix- commented on May 3, 2024

The problem right now is that to specify multiple levels of verbosity, for example, you have to add -v -v -v -v (-vvvv does not work). That's not very GNU-like at all.

Can we at least adopt the ability to do -vvvv even though we keep [true, true, true, true]?


FWIW, specially handling booleans is pretty universally constant. That is to say, just about every argument parser in existence that handles non-argument boolean flags has to support them specially. I don't think it's too much to understand that [Boolean] has special handling.

Getting back [true, true, true, true, true] where the true will never be anything other than true is what's less elegant in my opinion. The only meaningful information you'd get from [Boolean] arguments is by using .length.

from arg.

blakeembrey avatar blakeembrey commented on May 3, 2024

@Qix- I do agree by the way! How do you feel about the constant instead of overloading the built-in Boolean function? Is there a name for this behavior? It could just be:

arg({
  '-x': arg.BOOL // `arg.COUNT`, `arg.POS`, `arg.SEEN`, `arg.EXISTS`, `arg.<name of behavior>`
})

I just noticed the third argument too:

The previous value for the destination (useful for reduce-like operatons or for supporting -v multiple times, etc.)

This could be supported by providing arg.COUNT which takes over Boolean and does (_, _, i = 0) => i + 1.

from arg.

Qix- avatar Qix- commented on May 3, 2024

@blakeembrey I don't hate that, though I feel strongly they should be additional rather than first-class (i.e. the parser shouldn't be aware they even exist).

I think I see your point about symbols now:

// issue #11 is now simply these two lines, and the branch to skip
// looking for an argument is dependent upon the type being a function (!!!)
// with the FLAG_SYM symbol.
//
// I make special note of function checks because the following would be
// invalid, but not checking for it would make the behavior inconsistent and erroneous:
//
//        arg({'--verbose': Boolean, '-v': arg.FLAG_TYPE('--verbose')});
//
// In fact, an assertion/throw can happen in the case of:
//
//        (typeof argType !== 'function' && argType[FLAG_SYM])
//
// EDIT: Alternatively, enforce the type checking in `arg.FLAG_TYPE()` since
// that check would happen less often and is probably better anyway.
const FLAG_SYM = Symbol('arg flag argument type');
arg.FLAG_TYPE = fn => { fn[FLAG_SYM] = true; };

// a new PR is opened to add standard helpers that build off of
// the existing argument type handler specification:
arg.COUNT = arg.FLAG_TYPE((_, _, v) => (v || 0) + 1);

arg.ENUM = map => v => {
    if (v in map) return map[v];
    throw new Error(`invalid argument value: ${v}`);
};

// etc...

I think having some standard helpers would be nice to have. I'm not sure how far outside of the 'unix philosophy' this takes the package, though. @rauchg thoughts?

from arg.

blakeembrey avatar blakeembrey commented on May 3, 2024

@Qix- That's a good point. In that case, array support is just which might make that case easier:

function of (fn) {
  return (value, argName, prev = []) {
    prev.push(fn(value))
    return prev
  }
}

arg({
  '-x': of(String)
})

from arg.

Qix- avatar Qix- commented on May 3, 2024

@blakeembrey Right, but that means losing the simplicity of [Type].

Existing code would have to migrate to something like:

const arg = require('arg');

const args = arg({
    '--include': arg.ARRAY(String),
    '-I': '--include'
});

For comparison, the existing spec which is simpler to write looks like:

const args = arg({
    '--include': [String],
    '-I': '--include'
});

from arg.

pacocoursey avatar pacocoursey commented on May 3, 2024

If we want to keep the simplicity of [Type] but avoid the redundancy of returning an array that will only ever contain true values, how about faking it by using [Boolean].length?

This correctly specifies that a number will be returned, and looking at the code correctly implicates that it will return the length of the boolean array. It feels like a hack (and you could just type 1 instead of [Boolean].length) but it would work. Thoughts?

// ./my-program -vv -xxx

const args = arg({
    '-v': [Boolean].length,
    '-x': [Boolean]
});

/*
{
    '-v': 2,
    '-x': [true, true, true]
}
*/

from arg.

Qix- avatar Qix- commented on May 3, 2024

That feels like a hack :/ I'd say -1 on it.

I still think the symbol and helper method is the way to go.

from arg.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.