Skip to content

Commit

Permalink
Add TypeScript definition (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
BendingBender authored and sindresorhus committed Apr 11, 2019
1 parent 9265099 commit 24f4dd1
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 56 deletions.
136 changes: 136 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
declare namespace dargs {
interface Options {
/**
Keys or regex of keys to exclude. Takes precedence over `includes`.
*/
excludes?: ReadonlyArray<string | RegExp>;

/**
Keys or regex of keys to include.
*/
includes?: ReadonlyArray<string | RegExp>;

/**
Maps keys in `input` to an aliased name. Matching keys are converted to arguments with a single dash (`-`) in front of the aliased key and the value in a separate array item. Keys are still affected by `includes` and `excludes`.
*/
aliases?: {[key: string]: string};

/**
Setting this to `false` makes it return the key and value as separate array items instead of using a `=` separator in one item. This can be useful for tools that doesn't support `--foo=bar` style flags.
@default true
@example
```
console.log(dargs({foo: 'bar'}, {useEquals: false}));
// [
// '--foo', 'bar'
// ]
```
*/
useEquals?: boolean;

/**
Exclude `false` values. Can be useful when dealing with strict argument parsers that throw on unknown arguments like `--no-foo`.
@default false
*/
ignoreFalse?: boolean;

/**
By default, camelCased keys will be hyphenated. Enabling this will bypass the conversion process.
@default false
@example
```
console.log(dargs({fooBar: 'baz'}));
//=> ['--foo-bar', 'baz']
console.log(dargs({fooBar: 'baz'}, {allowCamelCase: true}));
//=> ['--fooBar', 'baz']
```
*/
allowCamelCase?: boolean;
}
}

/**
Reverse [`minimist`](https://github.com/substack/minimist). Convert an object of options into an array of command-line arguments.
@param input - Object to convert to command-line arguments.
@example
```
import dargs = require('dargs');
const input = {
_: ['some', 'option'], // Values in '_' will be appended to the end of the generated argument list
'--': ['separated', 'option'], // Values in '--' will be put at the very end of the argument list after the escape option (`--`)
foo: 'bar',
hello: true, // Results in only the key being used
cake: false, // Prepends `no-` before the key
camelCase: 5, // CamelCase is slugged to `camel-case`
multiple: ['value', 'value2'], // Converted to multiple arguments
pieKind: 'cherry',
sad: ':('
};
const excludes = ['sad', /.*Kind$/]; // Excludes and includes accept regular expressions
const includes = ['camelCase', 'multiple', 'sad', /^pie.+/];
const aliases = {file: 'f'};
console.log(dargs(input, {excludes}));
// [
// '--foo=bar',
// '--hello',
// '--no-cake',
// '--camel-case=5',
// '--multiple=value',
// '--multiple=value2',
// 'some',
// 'option',
// '--',
// 'separated',
// 'option'
// ]
console.log(dargs(input, {excludes, includes}));
// [
// '--camel-case=5',
// '--multiple=value',
// '--multiple=value2'
// ]
console.log(dargs(input, {includes}));
// [
// '--camel-case=5',
// '--multiple=value',
// '--multiple=value2',
// '--pie-kind=cherry',
// '--sad=:('
// ]
console.log(dargs({
foo: 'bar',
hello: true,
file: 'baz'
}, {aliases}));
// [
// '--foo=bar',
// '--hello',
// '-f', 'baz'
// ]
```
*/
declare function dargs(
input: {
'--'?: string[];
_?: string[];
} & {[key: string]: string | boolean | number | string[]},
options?: dargs.Options
): string[];

export = dargs;
75 changes: 43 additions & 32 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,92 +1,101 @@
'use strict';

const match = (array, value) => array.some(x => x instanceof RegExp ? x.test(value) : x === value);
const match = (array, value) =>
array.some(x => (x instanceof RegExp ? x.test(value) : x === value));

module.exports = (input, opts) => {
const dargs = (input, options) => {
const args = [];
let extraArgs = [];
let separatedArgs = [];

opts = Object.assign({
options = Object.assign({
useEquals: true
}, opts);
}, options);

const makeArg = (key, val) => {
key = '--' + (opts.allowCamelCase ? key : key.replace(/[A-Z]/g, '-$&').toLowerCase());
const makeArg = (key, value) => {
key =
'--' +
(options.allowCamelCase ?
key :
key.replace(/[A-Z]/g, '-$&').toLowerCase());

if (opts.useEquals) {
args.push(key + (val ? `=${val}` : ''));
if (options.useEquals) {
args.push(key + (value ? `=${value}` : ''));
} else {
args.push(key);

if (val) {
args.push(val);
if (value) {
args.push(value);
}
}
};

const makeAliasArg = (key, val) => {
const makeAliasArg = (key, value) => {
args.push(`-${key}`);

if (val) {
args.push(val);
if (value) {
args.push(value);
}
};

// TODO: Use Object.entries() when targeting Node.js 8
for (let key of Object.keys(input)) {
const val = input[key];
const value = input[key];
let pushArg = makeArg;

if (Array.isArray(opts.excludes) && match(opts.excludes, key)) {
if (Array.isArray(options.excludes) && match(options.excludes, key)) {
continue;
}

if (Array.isArray(opts.includes) && !match(opts.includes, key)) {
if (Array.isArray(options.includes) && !match(options.includes, key)) {
continue;
}

if (typeof opts.aliases === 'object' && opts.aliases[key]) {
key = opts.aliases[key];
if (typeof options.aliases === 'object' && options.aliases[key]) {
key = options.aliases[key];
pushArg = makeAliasArg;
}

if (key === '--') {
if (!Array.isArray(val)) {
throw new TypeError(`Expected key \`--\` to be Array, got ${typeof val}`);
if (!Array.isArray(value)) {
throw new TypeError(
`Expected key \`--\` to be Array, got ${typeof value}`
);
}

separatedArgs = val;
separatedArgs = value;
continue;
}

if (key === '_') {
if (!Array.isArray(val)) {
throw new TypeError(`Expected key \`_\` to be Array, got ${typeof val}`);
if (!Array.isArray(value)) {
throw new TypeError(
`Expected key \`_\` to be Array, got ${typeof value}`
);
}

extraArgs = val;
extraArgs = value;
continue;
}

if (val === true) {
if (value === true) {
pushArg(key, '');
}

if (val === false && !opts.ignoreFalse) {
if (value === false && !options.ignoreFalse) {
pushArg(`no-${key}`);
}

if (typeof val === 'string') {
pushArg(key, val);
if (typeof value === 'string') {
pushArg(key, value);
}

if (typeof val === 'number' && !Number.isNaN(val)) {
pushArg(key, String(val));
if (typeof value === 'number' && !Number.isNaN(value)) {
pushArg(key, String(value));
}

if (Array.isArray(val)) {
for (const arrayValue of val) {
if (Array.isArray(value)) {
for (const arrayValue of value) {
pushArg(key, arrayValue);
}
}
Expand All @@ -106,3 +115,5 @@ module.exports = (input, opts) => {

return args;
};

module.exports = dargs;
27 changes: 27 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {expectType, expectError} from 'tsd';
import dargs = require('.');

const input = {
_: ['some', 'option'],
foo: 'bar',
hello: true,
cake: false,
camelCase: 5,
multiple: ['value', 'value2'],
pieKind: 'cherry',
sad: ':('
};

const excludes = ['sad', /.*Kind$/];
const includes = ['camelCase', 'multiple', 'sad', /^pie.*/];
const aliases = {file: 'f'};

expectType<string[]>(dargs(input, {excludes}));
expectType<string[]>(dargs(input, {includes}));
expectType<string[]>(dargs(input, {aliases}));
expectType<string[]>(dargs(input, {useEquals: false}));
expectType<string[]>(dargs(input, {ignoreFalse: true}));
expectType<string[]>(dargs(input, {allowCamelCase: true}));

expectError(dargs({_: 'foo'}));
expectError(dargs({'--': 'foo'}));
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
"node": ">=6"
},
"scripts": {
"test": "xo && ava"
"test": "xo && ava && tsd"
},
"files": [
"index.js"
"index.js",
"index.d.ts"
],
"keywords": [
"reverse",
Expand All @@ -42,7 +43,8 @@
"argv"
],
"devDependencies": {
"ava": "*",
"xo": "*"
"ava": "^1.4.1",
"tsd": "^0.7.2",
"xo": "^0.24.0"
}
}
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const dargs = require('dargs');

const input = {
_: ['some', 'option'], // Values in '_' will be appended to the end of the generated argument list
'--': ['separated', 'option'], // Values in '--' will be put at the very end of the argument list after the escape option (`--`)
foo: 'bar',
hello: true, // Results in only the key being used
cake: false, // Prepends `no-` before the key
Expand All @@ -42,6 +43,9 @@ console.log(dargs(input, {excludes}));
'--multiple=value',
'--multiple=value2',
'some',
'option',
'--',
'separated',
'option'
]
*/
Expand Down
Loading

0 comments on commit 24f4dd1

Please sign in to comment.