Skip to content

Commit

Permalink
Add TypeScript definition (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
BendingBender authored and sindresorhus committed Mar 19, 2019
1 parent f36715c commit f1036df
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 10 deletions.
198 changes: 198 additions & 0 deletions index.d.ts
@@ -0,0 +1,198 @@
import {PackageJson} from 'type-fest';
import {Options as MinimistOptions} from 'minimist-options';

export interface Options {
/**
Define argument flags.
The key is the flag name and the value is an object with any of:
- `type`: Type of value. (Possible values: `string` `boolean`)
- `alias`: Usually used to define a short flag alias.
- `default`: Default value when the flag is not specified.
@example
```
flags: {
unicorn: {
type: 'string',
alias: 'u',
default: 'rainbow'
}
}
```
*/
readonly flags?: MinimistOptions;

/**
Description to show above the help text. Default: The package.json `"description"` property.
Set it to `false` to disable it altogether.
*/
readonly description?: string | false;

/**
The help text you want shown.
The input is reindented and starting/ending newlines are trimmed which means you can use a [template literal](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/template_strings) without having to care about using the correct amount of indent.
The description will be shown above your help text automatically.
Set it to `false` to disable it altogether.
*/
readonly help?: string | false;

/**
Set a custom version output. Default: The package.json `"version"` property.
Set it to `false` to disable it altogether.
*/
readonly version?: string | false;

/**
Automatically show the help text when the `--help` flag is present. Useful to set this value to `false` when a CLI manages child CLIs with their own help text.
*/
readonly autoHelp?: boolean;

/**
Automatically show the version text when the `--version` flag is present. Useful to set this value to `false` when a CLI manages child CLIs with their own version text.
*/
readonly autoVersion?: boolean;

/**
package.json as an `Object`. Default: Closest package.json upwards.
_You most likely don't need this option._
*/
readonly pkg?: {[key: string]: unknown};

/**
Custom arguments object.
@default process.argv.slice(2)
*/
readonly argv?: ReadonlyArray<string>;

/**
Infer the argument type.
By default, the argument `5` in `$ foo 5` becomes a string. Enabling this would infer it as a number.
@default false
*/
readonly inferType?: boolean;

/**
Value of `boolean` flags not defined in `argv`. If set to `undefined` the flags not defined in `argv` will be excluded from the result. The `default` value set in `boolean` flags take precedence over `booleanDefault`.
__Caution: Explicitly specifying undefined for `booleanDefault` has different meaning from omitting key itself.__
@example
```
const cli = meow(`
Usage
$ foo
Options
--rainbow, -r Include a rainbow
--unicorn, -u Include a unicorn
--no-sparkles Exclude sparkles
Examples
$ foo
🌈 unicorns✨🌈
`, {
booleanDefault: undefined,
flags: {
rainbow: {
type: 'boolean',
default: true,
alias: 'r'
},
unicorn: {
type: 'boolean',
default: false,
alias: 'u'
},
cake: {
type: 'boolean',
alias: 'c'
},
sparkles: {
type: 'boolean',
default: true
}
}
});
//{
// flags: {
// rainbow: true,
// unicorn: false,
// sparkles: true
// },
// unnormalizedFlags: {
// rainbow: true,
// r: true,
// unicorn: false,
// u: false,
// sparkles: true
// },
// …
//}
```
*/
readonly booleanDefault?: boolean | null | undefined;

/**
Whether to use [hard-rejection](https://github.com/sindresorhus/hard-rejection) or not. Disabling this can be useful if you need to handle `process.on('unhandledRejection')` yourself.
@default true
*/
readonly hardRejection?: boolean;
}

export interface Result {
/**
Non-flag arguments.
*/
input: string[];

/**
Flags converted to camelCase excluding aliases.
*/
flags: {[name: string]: unknown};

/**
Flags converted camelCase including aliases.
*/
unnormalizedFlags: {[name: string]: unknown};

/**
The `package.json` object.
*/
pkg: PackageJson;

/**
The help text used with `--help`.
*/
help: string;

/**
Show the help text and exit with code.
@param exitCode - The exit code to use. Default: `2`.
*/
showHelp(exitCode?: number): void;

/**
Show the version text and exit.
*/
showVersion(): void;
}

/**
@param helpMessage - Shortcut for the `help` option.
*/
export default function meow(helpMessage: string, options?: Options): Result;
export default function meow(options?: Options): Result;
5 changes: 4 additions & 1 deletion index.js
Expand Up @@ -14,7 +14,7 @@ const normalizePackageData = require('normalize-package-data');
delete require.cache[__filename];
const parentDir = path.dirname(module.parent.filename);

module.exports = (helpText, options) => {
const meow = (helpText, options) => {
if (typeof helpText !== 'string') {
options = helpText;
helpText = '';
Expand Down Expand Up @@ -126,3 +126,6 @@ module.exports = (helpText, options) => {
showVersion
};
};

module.exports = meow;
module.exports.default = meow;
47 changes: 47 additions & 0 deletions index.test-d.ts
@@ -0,0 +1,47 @@
import {expectType} from 'tsd-check';
import {PackageJson} from 'type-fest';
import meow, {Result} from '.';

expectType<Result>(meow('Help text'));
expectType<Result>(meow('Help text', {hardRejection: false}));
expectType<Result>(
meow({
flags: {
unicorn: {
type: 'boolean',
alias: 'u'
},
fooBar: {
type: 'string',
default: 'foo'
}
}
})
);
expectType<Result>(meow({description: 'foo'}));
expectType<Result>(meow({description: false}));
expectType<Result>(meow({help: 'foo'}));
expectType<Result>(meow({help: false}));
expectType<Result>(meow({version: 'foo'}));
expectType<Result>(meow({version: false}));
expectType<Result>(meow({autoHelp: false}));
expectType<Result>(meow({autoVersion: false}));
expectType<Result>(meow({pkg: {foo: 'bar'}}));
expectType<Result>(meow({argv: ['foo', 'bar']}));
expectType<Result>(meow({inferType: true}));
expectType<Result>(meow({booleanDefault: true}));
expectType<Result>(meow({booleanDefault: null}));
expectType<Result>(meow({booleanDefault: undefined}));
expectType<Result>(meow({hardRejection: false}));

const result = meow('Help text');

expectType<string[]>(result.input);
expectType<{[name: string]: unknown}>(result.flags);
expectType<{[name: string]: unknown}>(result.unnormalizedFlags);
expectType<PackageJson>(result.pkg);
expectType<string>(result.help);

result.showHelp();
result.showHelp(1);
result.showVersion();
20 changes: 12 additions & 8 deletions package.json
Expand Up @@ -13,10 +13,11 @@
"node": ">=8"
},
"scripts": {
"test": "xo && ava"
"test": "xo && ava && tsd-check"
},
"files": [
"index.js"
"index.js",
"index.d.ts"
],
"keywords": [
"cli",
Expand All @@ -38,21 +39,24 @@
"console"
],
"dependencies": {
"@types/minimist": "^1.2.0",
"camelcase-keys": "^5.0.0",
"decamelize-keys": "^1.0.0",
"decamelize-keys": "^1.1.0",
"hard-rejection": "^1.0.0",
"minimist-options": "^3.0.1",
"normalize-package-data": "^2.3.4",
"minimist-options": "^4.0.1",
"normalize-package-data": "^2.5.0",
"read-pkg-up": "^4.0.0",
"redent": "^2.0.0",
"trim-newlines": "^2.0.0",
"type-fest": "^0.2.0",
"yargs-parser": "^11.0.0"
},
"devDependencies": {
"ava": "^0.25.0",
"ava": "^1.3.1",
"execa": "^1.0.0",
"indent-string": "^3.0.0",
"xo": "^0.23.0"
"indent-string": "^3.2.0",
"tsd-check": "^0.3.0",
"xo": "^0.24.0"
},
"xo": {
"rules": {
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Expand Up @@ -77,7 +77,7 @@ Returns an `Object` with:
- `unnormalizedFlags` *(Object)* - Flags converted camelCase including aliases
- `pkg` *(Object)* - The `package.json` object
- `help` *(string)* - The help text used with `--help`
- `showHelp([code=2])` *(Function)* - Show the help text and exit with `code`
- `showHelp([exitCode=2])` *(Function)* - Show the help text and exit with `exitCode`
- `showVersion()` *(Function)* - Show the version text and exit

#### helpText
Expand Down

0 comments on commit f1036df

Please sign in to comment.