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

Provide an API to pass parameters which resemble options #792

Merged
merged 1 commit into from Oct 27, 2017

Conversation

nfischer
Copy link
Member

This adds the special option string --, which means "no options". This can be
passed if the first parameter looks like an option (starts with a - followed
by 1+ letters).

Fixes #778

This adds the special option string `--`, which means "no options". This can be
passed if the first parameter looks like an option (starts with a `-` followed
by 1+ letters).

Fixes #778
@nfischer nfischer added this to the v0.8.0 milestone Oct 20, 2017
@nfischer nfischer self-assigned this Oct 20, 2017
Copy link
Contributor

@freitagbr freitagbr left a comment

Choose a reason for hiding this comment

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

I'm not sure this implementation will work in all cases. Let's think of a different way to handle this.

If you need to pass a parameter that looks like an option, you can do so like:

```js
shell.grep('--', '-v', 'path/to/file'); // Search for "-v", no grep options
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmm... What if I want to grep recursively for '-v':

shell.grep('-r', '-v', 'path/to/dir');

In this case, '-v' will be treated as an option, which I don't want. So, using '--':

shell.grep('--', '-r', '-v', 'path/to/dir');

But now '-r' is not treated as an option, which I also don't want.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess, in the case grep specifically, this is how you do the above:

$ grep -r -- -v path/to/dir

So, -- is telling grep to take the next argument literally, as opposed to taking all arguments literally.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, see the added docs. The correct way to do what you describe is grep('-r', '-v', 'path/to/dir');

Copy link
Contributor

Choose a reason for hiding this comment

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

Of course. I forgot that all arguments must be passed at once.

if (opt === '--') {
// This means there are no options.
return {};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Given my realization above, what we need to do here is just ignore the next argument if '--' is present. This could be a bit tricky to implement though, looking at the way parseOptions works.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, there's at-most 1 option string in shelljs. That means that cp('-rf', 'foo', 'dir') is different than cp('-r', '-f', 'foo', 'dir'). The former copies one dir recursively and forcibly, the latter recursively copies 2 dirs named -f and foo into dir.

@freitagbr
Copy link
Contributor

LGTM.

@nfischer
Copy link
Member Author

Maybe there's something better we can do here. This makes sense for the no-options case:

shell.grep('--', '-v', 'path/to/file.txt'); // just like unix, woohoo!

But I agree this is a little confusing for the other case:

shell.grep('-r', '-v', 'path/to/dir/'); // it looks like we're passing 2 flags, but '-v' is our regex string

// even worse, this is invalid shelljs code:
shell.grep('-r', '--', '-v', 'path/to/dir/'); // even though it looks like valid (but verbose) unix

Do you think this API is sufficient? Is there a way to improve the readability of example 2? Should we allow example 3?

@freitagbr
Copy link
Contributor

I would say that because all of the flags must be passed in one string, that example 1 and example 2 are fine. But you are right, it does get confusing when example 3 would be valid in the shell but is not valid here.

Maybe at some point in the future, we could allow flags to be passed in separately. But for now, I think this implementation is fine.

If you're wondering, this is how I might implement allowing parameters that look like options, without using another argument (if we allowed passing in multiple options):

// represents a literal value that might look like an option, like "-v"
function Literal(value) {
    this.value = value;
}

// convenience method
function literal(value) {
    return new Literal(value);
}

// some future implementation of parseOptions that handles
// multiple option arguments
function parseOptions(opts, map, errorOptions) {
    // ...
    opts.forEach(function (opt) {
        // ignore the opt if we want to treat it literally
        if (opt instanceof Literal) return;
    });
    // ...
}

// usage
shell.grep('-r', shell.literal('-v'), 'path/to/dir');

@nfischer
Copy link
Member Author

Probably fine as-is. I like the shell.literal() idea. We might consider adding that in v0.9 (it would be a nice way to pass * as a literal argument, too).

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.

None yet

2 participants