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(parser): support short-option groups #59

Merged
merged 4 commits into from
Feb 6, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ const { flags, values, positionals } = parseArgs(argv, options);
- Does the API specify whether a `--` was present/relevant?
- no
- Is `-foo` the same as `--foo`?
bcoe marked this conversation as resolved.
Show resolved Hide resolved
- no, `-foo` is a short option or options (WIP: https://github.com/pkgjs/parseargs/issues/2)
- no, `-foo` is a short option or options, with expansion logic that follows the
bcoe marked this conversation as resolved.
Show resolved Hide resolved
[Utility Syntax Guidelines in POSIX.1-2017](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html). `--foo bar`, `withValue: ['foo']`, expands to `-f`, `-o`, `-o bar`.
bcoe marked this conversation as resolved.
Show resolved Hide resolved
- Is `---foo` the same as `--foo`?
- no
- the first flag would be parsed as `'-foo'`
Expand Down
8 changes: 0 additions & 8 deletions errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,8 @@ class ERR_INVALID_ARG_TYPE extends TypeError {
}
}

class ERR_NOT_IMPLEMENTED extends Error {
constructor(feature) {
super(`${feature} not implemented`);
this.code = 'ERR_NOT_IMPLEMENTED';
}
}

module.exports = {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_NOT_IMPLEMENTED
}
};
16 changes: 8 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const {
ArrayPrototypeConcat,
ArrayPrototypeIncludes,
ArrayPrototypeSlice,
ArrayPrototypeSplice,
ArrayPrototypePush,
ObjectHasOwn,
StringPrototypeCharAt,
Expand All @@ -13,12 +14,6 @@ const {
StringPrototypeStartsWith,
} = require('./primordials');

const {
codes: {
ERR_NOT_IMPLEMENTED
}
} = require('./errors');

const {
validateArray,
validateObject
Expand Down Expand Up @@ -119,9 +114,14 @@ const parseArgs = (
);
return result;
} else if (StringPrototypeCharAt(arg, 1) !== '-') {
// Look for shortcodes: -fXzy
// Look for shortcodes: -fXzy and expand them to -f -X -z -y:
if (arg.length > 2) {
throw new ERR_NOT_IMPLEMENTED('short option groups');
for (let i = 2; i < arg.length; i++) {
const short = StringPrototypeCharAt(arg, i);
// Add 'i' to 'pos' such that short options are parsed in order
// of definition:
ArrayPrototypeSplice(argv, pos + (i - 1), 0, `-${short}`);
}
bcoe marked this conversation as resolved.
Show resolved Hide resolved
}

arg = StringPrototypeCharAt(arg, 1); // short
Expand Down
52 changes: 52 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,58 @@ test('when short option withValue used without value then stored as flag', funct
t.end();
});

test('short option group behaves like multiple short options', function(t) {
const passedArgs = ['-rf'];
const passedOptions = { };
const expected = { flags: { r: true, f: true }, values: { r: undefined, f: undefined }, positionals: [] };
const args = parseArgs(passedArgs, passedOptions);

t.deepEqual(args, expected);

t.end();
});

test('short option group does not consume subsequent positional', function(t) {
const passedArgs = ['-rf', 'foo'];
const passedOptions = { };
const expected = { flags: { r: true, f: true }, values: { r: undefined, f: undefined }, positionals: ['foo'] };
const args = parseArgs(passedArgs, passedOptions);
t.deepEqual(args, expected);

t.end();
});

// See: Guideline 5 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
test('if terminal of short-option group configured withValue, subsequent positional is stored', function(t) {
const passedArgs = ['-rvf', 'foo'];
const passedOptions = { withValue: ['f'] };
const expected = { flags: { r: true, f: true, v: true }, values: { r: undefined, v: undefined, f: 'foo' }, positionals: [] };
const args = parseArgs(passedArgs, passedOptions);
t.deepEqual(args, expected);

t.end();
});

test('handles short-option groups in conjunction with long-options', function(t) {
const passedArgs = ['-rf', '--foo', 'foo'];
const passedOptions = { withValue: ['foo'] };
const expected = { flags: { r: true, f: true, foo: true }, values: { r: undefined, f: undefined, foo: 'foo' }, positionals: [] };
const args = parseArgs(passedArgs, passedOptions);
t.deepEqual(args, expected);

t.end();
});

test('handles short-option groups with "short" alias configured', function(t) {
const passedArgs = ['-rf'];
const passedOptions = { short: { r: 'remove' } };
const expected = { flags: { remove: true, f: true }, values: { remove: undefined, f: undefined }, positionals: [] };
const args = parseArgs(passedArgs, passedOptions);
t.deepEqual(args, expected);

t.end();
});

test('Everything after a bare `--` is considered a positional argument', function(t) {
const passedArgs = ['--', 'barepositionals', 'mopositionals'];
const expected = { flags: {}, values: {}, positionals: ['barepositionals', 'mopositionals'] };
Expand Down