From 73f160af5d30ec4e6763945373632d96965585ee Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 11:23:30 +0800 Subject: [PATCH 01/11] Require Node.js 12 and move to ESM --- .github/workflows/main.yml | 2 +- estest/index.js | 4 +-- index.d.ts | 11 +++--- index.js | 31 +++++++--------- index.test-d.ts | 37 ++++++++++---------- package.json | 11 +++--- test/allow-unkonwn-flags.js | 2 ++ test/fixtures/fixture-allow-unknown-flags.js | 3 +- test/fixtures/fixture-required-function.js | 3 +- test/fixtures/fixture-required-multiple.js | 3 +- test/fixtures/fixture-required.js | 2 +- test/fixtures/fixture.js | 7 ++-- test/is-required-flag.js | 4 ++- test/test.js | 7 ++-- 14 files changed, 63 insertions(+), 64 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c1870cf..48143fc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,9 +10,9 @@ jobs: fail-fast: false matrix: node-version: + - 16 - 14 - 12 - - 10 steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 diff --git a/estest/index.js b/estest/index.js index b4d1af9..64e1e64 100644 --- a/estest/index.js +++ b/estest/index.js @@ -1,6 +1,6 @@ -import {createRequire} from 'module'; +import meow from '../index.js'; -const meow = createRequire(import.meta.url)('../index.js'); +console.log(import.meta.url); meow(` Usage diff --git a/index.d.ts b/index.d.ts index 59492ad..1e31987 100644 --- a/index.d.ts +++ b/index.d.ts @@ -138,7 +138,7 @@ declare namespace meow { @example ``` - import meow = require('meow'); + import meow from 'meow'; const cli = meow(` Usage @@ -277,9 +277,8 @@ declare namespace meow { @example ``` #!/usr/bin/env node -'use strict'; -import meow = require('meow'); -import foo = require('.'); +import meow = from 'meow'; +import foo from './index.js'; const cli = meow(` Usage @@ -309,7 +308,9 @@ const cli = meow(` foo(cli.input[0], cli.flags); ``` */ +// eslint-disable-next-line no-redeclare declare function meow(helpMessage: string, options?: meow.Options): meow.Result; +// eslint-disable-next-line no-redeclare declare function meow(options?: meow.Options): meow.Result; -export = meow; +export default meow; diff --git a/index.js b/index.js index f9a6423..123782a 100644 --- a/index.js +++ b/index.js @@ -1,19 +1,13 @@ -'use strict'; -const path = require('path'); -const buildParserOptions = require('minimist-options'); -const parseArguments = require('yargs-parser'); -const camelCaseKeys = require('camelcase-keys'); -const decamelize = require('decamelize'); -const decamelizeKeys = require('decamelize-keys'); -const trimNewlines = require('trim-newlines'); -const redent = require('redent'); -const readPkgUp = require('read-pkg-up'); -const hardRejection = require('hard-rejection'); -const normalizePackageData = require('normalize-package-data'); - -// Prevent caching of this module so module.parent is always accurate -delete require.cache[__filename]; -const parentDir = path.dirname(module.parent && module.parent.filename ? module.parent.filename : '.'); +import buildParserOptions from 'minimist-options'; +import parseArguments from 'yargs-parser'; +import camelCaseKeys from 'camelcase-keys'; +import decamelize from 'decamelize'; +import decamelizeKeys from 'decamelize-keys'; +import trimNewlines from 'trim-newlines'; +import redent from 'redent'; +import {readPackageUpSync} from 'read-pkg-up'; +import hardRejection from 'hard-rejection'; +import normalizePackageData from 'normalize-package-data'; const isFlagMissing = (flagName, definedFlags, receivedFlags, input) => { const flag = definedFlags[flagName]; @@ -109,8 +103,7 @@ const meow = (helpText, options) => { helpText = ''; } - const foundPkg = readPkgUp.sync({ - cwd: parentDir, + const foundPkg = readPackageUpSync({ normalize: false }); @@ -231,4 +224,4 @@ const meow = (helpText, options) => { }; }; -module.exports = meow; +export default meow; diff --git a/index.test-d.ts b/index.test-d.ts index 4e7da4a..0043316 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,10 +1,9 @@ import {expectAssignable, expectType} from 'tsd'; import {PackageJson} from 'type-fest'; -import meow = require('.'); -import {Result} from '.'; +import meow from './index.js'; -expectType>(meow('Help text')); -expectType>(meow('Help text', {hardRejection: false})); +expectType>(meow('Help text')); +expectType>(meow('Help text', {hardRejection: false})); expectAssignable<{flags: {foo: number}}>( meow({flags: {foo: {type: 'number', isRequired: true}}}) ); @@ -23,21 +22,21 @@ expectAssignable<{flags: {foo: string | undefined}}>( expectAssignable<{flags: {foo: boolean | undefined}}>( meow({flags: {foo: {type: 'boolean'}}}) ); -expectType>(meow({description: 'foo'})); -expectType>(meow({description: false})); -expectType>(meow({help: 'foo'})); -expectType>(meow({help: false})); -expectType>(meow({version: 'foo'})); -expectType>(meow({version: false})); -expectType>(meow({autoHelp: false})); -expectType>(meow({autoVersion: false})); -expectType>(meow({pkg: {foo: 'bar'}})); -expectType>(meow({argv: ['foo', 'bar']})); -expectType>(meow({inferType: true})); -expectType>(meow({booleanDefault: true})); -expectType>(meow({booleanDefault: null})); -expectType>(meow({booleanDefault: undefined})); -expectType>(meow({hardRejection: false})); +expectType>(meow({description: 'foo'})); +expectType>(meow({description: false})); +expectType>(meow({help: 'foo'})); +expectType>(meow({help: false})); +expectType>(meow({version: 'foo'})); +expectType>(meow({version: false})); +expectType>(meow({autoHelp: false})); +expectType>(meow({autoVersion: false})); +expectType>(meow({pkg: {foo: 'bar'}})); +expectType>(meow({argv: ['foo', 'bar']})); +expectType>(meow({inferType: true})); +expectType>(meow({booleanDefault: true})); +expectType>(meow({booleanDefault: null})); +expectType>(meow({booleanDefault: undefined})); +expectType>(meow({hardRejection: false})); const result = meow('Help text', { flags: { diff --git a/package.json b/package.json index 6875491..c47baf3 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": "./index.js", "engines": { - "node": ">=10" + "node": ">=12" }, "scripts": { "test": "xo && ava && tsd" @@ -47,18 +49,19 @@ "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", + "read-pkg-up": "^8.0.0", "redent": "^3.0.0", "trim-newlines": "^3.0.0", "type-fest": "^0.18.0", "yargs-parser": "^20.2.3" }, "devDependencies": { - "ava": "^2.4.0", + "ava": "^3.15.0", "execa": "^4.1.0", "indent-string": "^4.0.0", + "read-pkg": "^5.2.0", "tsd": "^0.13.1", - "xo": "^0.34.1" + "xo": "^0.39.1" }, "xo": { "rules": { diff --git a/test/allow-unkonwn-flags.js b/test/allow-unkonwn-flags.js index 9631bb7..7333b99 100644 --- a/test/allow-unkonwn-flags.js +++ b/test/allow-unkonwn-flags.js @@ -1,7 +1,9 @@ import path from 'path'; +import {fileURLToPath} from 'url'; import test from 'ava'; import execa from 'execa'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const fixtureAllowUnknownFlags = path.join(__dirname, 'fixtures', 'fixture-allow-unknown-flags.js'); test('spawn CLI and test specifying unknown flags', async t => { diff --git a/test/fixtures/fixture-allow-unknown-flags.js b/test/fixtures/fixture-allow-unknown-flags.js index 131e9e9..eb0c100 100755 --- a/test/fixtures/fixture-allow-unknown-flags.js +++ b/test/fixtures/fixture-allow-unknown-flags.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -'use strict'; -const meow = require('../..'); +import meow from '../../index.js'; const cli = meow({ description: 'Custom description', diff --git a/test/fixtures/fixture-required-function.js b/test/fixtures/fixture-required-function.js index ef3c443..c509254 100755 --- a/test/fixtures/fixture-required-function.js +++ b/test/fixtures/fixture-required-function.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -'use strict'; -const meow = require('../..'); +import meow from '../../index.js'; const cli = meow({ description: 'Custom description', diff --git a/test/fixtures/fixture-required-multiple.js b/test/fixtures/fixture-required-multiple.js index fa424e2..907ffc4 100755 --- a/test/fixtures/fixture-required-multiple.js +++ b/test/fixtures/fixture-required-multiple.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -'use strict'; -const meow = require('../..'); +import meow from '../../index.js'; const cli = meow({ description: 'Custom description', diff --git a/test/fixtures/fixture-required.js b/test/fixtures/fixture-required.js index 2f3690e..ad9341a 100755 --- a/test/fixtures/fixture-required.js +++ b/test/fixtures/fixture-required.js @@ -1,6 +1,6 @@ #!/usr/bin/env node 'use strict'; -const meow = require('../..'); +import meow from '../../index.js'; const cli = meow({ description: 'Custom description', diff --git a/test/fixtures/fixture.js b/test/fixtures/fixture.js index 6917bae..5fc0f17 100755 --- a/test/fixtures/fixture.js +++ b/test/fixtures/fixture.js @@ -1,6 +1,5 @@ #!/usr/bin/env node -'use strict'; -const meow = require('../..'); +import meow from '../../index.js'; const cli = meow({ description: 'Custom description', @@ -18,9 +17,9 @@ const cli = meow({ }); if (cli.flags.camelCaseOption === 'foo') { - Object.keys(cli.flags).forEach(x => { + for (const x of Object.keys(cli.flags)) { console.log(x); - }); + } } else { console.log(cli.flags.camelCaseOption); } diff --git a/test/is-required-flag.js b/test/is-required-flag.js index 4586b28..725b17c 100644 --- a/test/is-required-flag.js +++ b/test/is-required-flag.js @@ -1,7 +1,9 @@ import test from 'ava'; import execa from 'execa'; -const path = require('path'); +import path from 'path'; +import {fileURLToPath} from 'url'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const fixtureRequiredPath = path.join(__dirname, 'fixtures', 'fixture-required.js'); const fixtureRequiredFunctionPath = path.join(__dirname, 'fixtures', 'fixture-required-function.js'); const fixtureRequiredMultiplePath = path.join(__dirname, 'fixtures', 'fixture-required-multiple.js'); diff --git a/test/test.js b/test/test.js index c8dc19d..5b6b148 100644 --- a/test/test.js +++ b/test/test.js @@ -2,9 +2,11 @@ import test from 'ava'; import indentString from 'indent-string'; import execa from 'execa'; import path from 'path'; -import pkg from '../package.json'; -import meow from '..'; +import {fileURLToPath} from 'url'; +import readPkg from 'read-pkg'; +import meow from '../index.js'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const fixturePath = path.join(__dirname, 'fixtures', 'fixture.js'); const NODE_MAJOR_VERSION = process.versions.node.split('.')[0]; @@ -40,6 +42,7 @@ test('support help shortcut', t => { }); test('spawn cli and show version', async t => { + const pkg = await readPkg(); const {stdout} = await execa(fixturePath, ['--version']); t.is(stdout, pkg.version); }); From 1424ab6fa071c6319a91742d5f384c319c771925 Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 11:29:32 +0800 Subject: [PATCH 02/11] Drop tuning code --- estest/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/estest/index.js b/estest/index.js index 64e1e64..2992614 100644 --- a/estest/index.js +++ b/estest/index.js @@ -1,7 +1,5 @@ import meow from '../index.js'; -console.log(import.meta.url); - meow(` Usage $ estest From 25295012d3729047cdd847f238abd598a843fcc7 Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 11:46:56 +0800 Subject: [PATCH 03/11] Use setup-node@v2 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 48143fc..3b8aa86 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: - 12 steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm install From 68e5eedf36fd480dddceb94af2bf12f865cbf3b7 Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 12:01:37 +0800 Subject: [PATCH 04/11] Bump read-pkg@6.0.0 --- package.json | 2 +- test/test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c47baf3..d4caf34 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ava": "^3.15.0", "execa": "^4.1.0", "indent-string": "^4.0.0", - "read-pkg": "^5.2.0", + "read-pkg": "^6.0.0", "tsd": "^0.13.1", "xo": "^0.39.1" }, diff --git a/test/test.js b/test/test.js index 5b6b148..c5d0fea 100644 --- a/test/test.js +++ b/test/test.js @@ -3,7 +3,7 @@ import indentString from 'indent-string'; import execa from 'execa'; import path from 'path'; import {fileURLToPath} from 'url'; -import readPkg from 'read-pkg'; +import {readPackageAsync} from 'read-pkg'; import meow from '../index.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -42,7 +42,7 @@ test('support help shortcut', t => { }); test('spawn cli and show version', async t => { - const pkg = await readPkg(); + const pkg = await readPackageAsync(); const {stdout} = await execa(fixturePath, ['--version']); t.is(stdout, pkg.version); }); From 81bd0cd648fd5fe085b8f7b95e92490a14e663c7 Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 18:15:03 +0800 Subject: [PATCH 05/11] Use the `node:` protocol for imports Co-authored-by: Sindre Sorhus --- test/is-required-flag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/is-required-flag.js b/test/is-required-flag.js index 725b17c..4f5e7ed 100644 --- a/test/is-required-flag.js +++ b/test/is-required-flag.js @@ -1,6 +1,6 @@ import test from 'ava'; import execa from 'execa'; -import path from 'path'; +import path from 'node:path'; import {fileURLToPath} from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); From c603cb671f29ddd5ef6fcb34640cd0194ef7873e Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 20:25:11 +0800 Subject: [PATCH 06/11] Remove declares --- index.d.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index 1e31987..2c23a66 100644 --- a/index.d.ts +++ b/index.d.ts @@ -308,9 +308,5 @@ const cli = meow(` foo(cli.input[0], cli.flags); ``` */ -// eslint-disable-next-line no-redeclare -declare function meow(helpMessage: string, options?: meow.Options): meow.Result; -// eslint-disable-next-line no-redeclare -declare function meow(options?: meow.Options): meow.Result; export default meow; From 9c125bcf6bf11de5c9a29c99f8977a2b34b57e06 Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 20:38:14 +0800 Subject: [PATCH 07/11] Update declaration file --- index.d.ts | 122 ++++++++++++++++++++++++------------------------ index.test-d.ts | 36 +++++++------- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/index.d.ts b/index.d.ts index 2c23a66..d343463 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,9 +1,8 @@ import {PackageJson} from 'type-fest'; -declare namespace meow { - type FlagType = 'string' | 'boolean' | 'number'; +export type FlagType = 'string' | 'boolean' | 'number'; - /** +/** Callback function to determine if a flag is required during runtime. @param flags - Contains the flags converted to camel-case excluding aliases. @@ -11,15 +10,15 @@ declare namespace meow { @returns True if the flag is required, otherwise false. */ - type IsRequiredPredicate = (flags: Readonly, input: readonly string[]) => boolean; - - interface Flag { - readonly type?: Type; - readonly alias?: string; - readonly default?: Default; - readonly isRequired?: boolean | IsRequiredPredicate; - readonly isMultiple?: boolean; - } +export type IsRequiredPredicate = (flags: Readonly, input: readonly string[]) => boolean; + +export interface Flag { + readonly type?: Type; + readonly alias?: string; + readonly default?: Default; + readonly isRequired?: boolean | IsRequiredPredicate; + readonly isMultiple?: boolean; +} type StringFlag = Flag<'string', string>; type BooleanFlag = Flag<'boolean', boolean>; @@ -28,8 +27,8 @@ declare namespace meow { type AnyFlag = StringFlag | BooleanFlag | NumberFlag; type AnyFlags = Record; - interface Options { - /** +export interface Options { + /** Define argument flags. The key is the flag name in camel-case and the value is an object with any of: @@ -63,16 +62,16 @@ declare namespace meow { } ``` */ - readonly flags?: Flags; + readonly flags?: Flags; - /** + /** 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; + 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. @@ -81,53 +80,53 @@ declare namespace meow { Set it to `false` to disable it altogether. */ - readonly help?: string | false; + 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; + 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. This option is only considered when there is only one argument in `process.argv`. */ - readonly autoHelp?: boolean; + 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. This option is only considered when there is only one argument in `process.argv`. */ - readonly autoVersion?: boolean; + readonly autoVersion?: boolean; - /** + /** `package.json` as an `Object`. Default: Closest `package.json` upwards. _You most likely don't need this option._ */ - readonly pkg?: Record; + readonly pkg?: Record; - /** + /** Custom arguments object. @default process.argv.slice(2) */ - readonly argv?: readonly string[]; + readonly argv?: readonly 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; + 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`. @@ -193,24 +192,24 @@ declare namespace meow { //} ``` */ - readonly booleanDefault?: boolean | null | undefined; + 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; + readonly hardRejection?: boolean; - /** + /** Whether to allow unknown flags or not. @default true */ - readonly allowUnknownFlags?: boolean; - } + readonly allowUnknownFlags?: boolean; +} - type TypedFlag = +export type TypedFlag = Flag extends {type: 'number'} ? number : Flag extends {type: 'string'} @@ -219,57 +218,56 @@ declare namespace meow { ? boolean : unknown; - type PossiblyOptionalFlag = +export type PossiblyOptionalFlag = Flag extends {isRequired: true} ? FlagType : Flag extends {default: any} ? FlagType : FlagType | undefined; - type TypedFlags = { - [F in keyof Flags]: Flags[F] extends {isMultiple: true} - ? PossiblyOptionalFlag>> - : PossiblyOptionalFlag> - }; +export type TypedFlags = { + [F in keyof Flags]: Flags[F] extends {isMultiple: true} + ? PossiblyOptionalFlag>> + : PossiblyOptionalFlag> +}; - interface Result { - /** +export interface Result { + /** Non-flag arguments. */ - input: string[]; + input: string[]; - /** + /** Flags converted to camelCase excluding aliases. */ - flags: TypedFlags & Record; + flags: TypedFlags & Record; - /** + /** Flags converted camelCase including aliases. */ - unnormalizedFlags: TypedFlags & Record; + unnormalizedFlags: TypedFlags & Record; - /** + /** The `package.json` object. */ - pkg: PackageJson; + pkg: PackageJson; - /** + /** The help text used with `--help`. */ - help: string; + help: string; - /** + /** Show the help text and exit with code. @param exitCode - The exit code to use. Default: `2`. */ - showHelp: (exitCode?: number) => void; + showHelp: (exitCode?: number) => void; - /** + /** Show the version text and exit. */ - showVersion: () => void; - } + showVersion: () => void; } /** @param helpMessage - Shortcut for the `help` option. @@ -309,4 +307,6 @@ foo(cli.input[0], cli.flags); ``` */ +declare function meow(helpMessage: string | Options, options?: Options): Result; + export default meow; diff --git a/index.test-d.ts b/index.test-d.ts index 0043316..5c154d2 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,9 +1,9 @@ import {expectAssignable, expectType} from 'tsd'; import {PackageJson} from 'type-fest'; -import meow from './index.js'; +import meow, {Result} from './index.js'; -expectType>(meow('Help text')); -expectType>(meow('Help text', {hardRejection: false})); +expectType>(meow('Help text')); +expectType>(meow('Help text', {hardRejection: false})); expectAssignable<{flags: {foo: number}}>( meow({flags: {foo: {type: 'number', isRequired: true}}}) ); @@ -22,21 +22,21 @@ expectAssignable<{flags: {foo: string | undefined}}>( expectAssignable<{flags: {foo: boolean | undefined}}>( meow({flags: {foo: {type: 'boolean'}}}) ); -expectType>(meow({description: 'foo'})); -expectType>(meow({description: false})); -expectType>(meow({help: 'foo'})); -expectType>(meow({help: false})); -expectType>(meow({version: 'foo'})); -expectType>(meow({version: false})); -expectType>(meow({autoHelp: false})); -expectType>(meow({autoVersion: false})); -expectType>(meow({pkg: {foo: 'bar'}})); -expectType>(meow({argv: ['foo', 'bar']})); -expectType>(meow({inferType: true})); -expectType>(meow({booleanDefault: true})); -expectType>(meow({booleanDefault: null})); -expectType>(meow({booleanDefault: undefined})); -expectType>(meow({hardRejection: false})); +expectType>(meow({description: 'foo'})); +expectType>(meow({description: false})); +expectType>(meow({help: 'foo'})); +expectType>(meow({help: false})); +expectType>(meow({version: 'foo'})); +expectType>(meow({version: false})); +expectType>(meow({autoHelp: false})); +expectType>(meow({autoVersion: false})); +expectType>(meow({pkg: {foo: 'bar'}})); +expectType>(meow({argv: ['foo', 'bar']})); +expectType>(meow({inferType: true})); +expectType>(meow({booleanDefault: true})); +expectType>(meow({booleanDefault: null})); +expectType>(meow({booleanDefault: undefined})); +expectType>(meow({hardRejection: false})); const result = meow('Help text', { flags: { From 255ae0de08ad2cd6220e62389d353a6714bf3d73 Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 21:20:48 +0800 Subject: [PATCH 08/11] Use `node:` imports for built-in modules --- test/allow-unkonwn-flags.js | 4 ++-- test/is-required-flag.js | 4 ++-- test/test.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/allow-unkonwn-flags.js b/test/allow-unkonwn-flags.js index 7333b99..fc723df 100644 --- a/test/allow-unkonwn-flags.js +++ b/test/allow-unkonwn-flags.js @@ -1,5 +1,5 @@ -import path from 'path'; -import {fileURLToPath} from 'url'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; import test from 'ava'; import execa from 'execa'; diff --git a/test/is-required-flag.js b/test/is-required-flag.js index 4f5e7ed..45f4ca9 100644 --- a/test/is-required-flag.js +++ b/test/is-required-flag.js @@ -1,7 +1,7 @@ +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; import test from 'ava'; import execa from 'execa'; -import path from 'node:path'; -import {fileURLToPath} from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const fixtureRequiredPath = path.join(__dirname, 'fixtures', 'fixture-required.js'); diff --git a/test/test.js b/test/test.js index c5d0fea..0487543 100644 --- a/test/test.js +++ b/test/test.js @@ -1,8 +1,8 @@ +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; import test from 'ava'; import indentString from 'indent-string'; import execa from 'execa'; -import path from 'path'; -import {fileURLToPath} from 'url'; import {readPackageAsync} from 'read-pkg'; import meow from '../index.js'; From c85d34d9ea4e08182b3c1b413a6f28b71640b8d7 Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 22:56:51 +0800 Subject: [PATCH 09/11] Fixes --- index.d.ts | 246 ++++++++++++++++++++++++++--------------------------- 1 file changed, 123 insertions(+), 123 deletions(-) diff --git a/index.d.ts b/index.d.ts index d343463..dec20b5 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,12 +3,12 @@ import {PackageJson} from 'type-fest'; export type FlagType = 'string' | 'boolean' | 'number'; /** - Callback function to determine if a flag is required during runtime. +Callback function to determine if a flag is required during runtime. - @param flags - Contains the flags converted to camel-case excluding aliases. - @param input - Contains the non-flag arguments. +@param flags - Contains the flags converted to camel-case excluding aliases. +@param input - Contains the non-flag arguments. - @returns True if the flag is required, otherwise false. +@returns True if the flag is required, otherwise false. */ export type IsRequiredPredicate = (flags: Readonly, input: readonly string[]) => boolean; @@ -29,182 +29,182 @@ export interface Flag { export interface Options { /** - Define argument flags. + Define argument flags. - The key is the flag name in camel-case and the value is an object with any of: + The key is the flag name in camel-case and the value is an object with any of: - - `type`: Type of value. (Possible values: `string` `boolean` `number`) - - `alias`: Usually used to define a short flag alias. - - `default`: Default value when the flag is not specified. - - `isRequired`: Determine if the flag is required. - If it's only known at runtime whether the flag is required or not you can pass a Function instead of a boolean, which based on the given flags and other non-flag arguments should decide if the flag is required. - - `isMultiple`: Indicates a flag can be set multiple times. Values are turned into an array. (Default: false) - Multiple values are provided by specifying the flag multiple times, for example, `$ foo -u rainbow -u cat`. Space- or comma-separated values are *not* supported. + - `type`: Type of value. (Possible values: `string` `boolean` `number`) + - `alias`: Usually used to define a short flag alias. + - `default`: Default value when the flag is not specified. + - `isRequired`: Determine if the flag is required. + If it's only known at runtime whether the flag is required or not you can pass a Function instead of a boolean, which based on the given flags and other non-flag arguments should decide if the flag is required. + - `isMultiple`: Indicates a flag can be set multiple times. Values are turned into an array. (Default: false) + Multiple values are provided by specifying the flag multiple times, for example, `$ foo -u rainbow -u cat`. Space- or comma-separated values are *not* supported. - Note that flags are always defined using a camel-case key (`myKey`), but will match arguments in kebab-case (`--my-key`). + Note that flags are always defined using a camel-case key (`myKey`), but will match arguments in kebab-case (`--my-key`). - @example - ``` - flags: { - unicorn: { - type: 'string', - alias: 'u', - default: ['rainbow', 'cat'], - isMultiple: true, - isRequired: (flags, input) => { - if (flags.otherFlag) { - return true; - } - - return false; + @example + ``` + flags: { + unicorn: { + type: 'string', + alias: 'u', + default: ['rainbow', 'cat'], + isMultiple: true, + isRequired: (flags, input) => { + if (flags.otherFlag) { + return true; } + + return false; } } - ``` + } + ``` */ readonly flags?: Flags; /** - Description to show above the help text. Default: The package.json `"description"` property. + Description to show above the help text. Default: The package.json `"description"` property. - Set it to `false` to disable it altogether. + Set it to `false` to disable it altogether. */ readonly description?: string | false; /** - The help text you want shown. + 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 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. + The description will be shown above your help text automatically. - Set it to `false` to disable it altogether. + Set it to `false` to disable it altogether. */ readonly help?: string | false; /** - Set a custom version output. Default: The package.json `"version"` property. + Set a custom version output. Default: The package.json `"version"` property. - Set it to `false` to disable it altogether. + 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. + 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. - This option is only considered when there is only one argument in `process.argv`. + This option is only considered when there is only one argument in `process.argv`. */ 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. + 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. - This option is only considered when there is only one argument in `process.argv`. + This option is only considered when there is only one argument in `process.argv`. */ readonly autoVersion?: boolean; /** - `package.json` as an `Object`. Default: Closest `package.json` upwards. + `package.json` as an `Object`. Default: Closest `package.json` upwards. - _You most likely don't need this option._ + _You most likely don't need this option._ */ readonly pkg?: Record; /** - Custom arguments object. + Custom arguments object. - @default process.argv.slice(2) + @default process.argv.slice(2) */ readonly argv?: readonly string[]; /** - Infer the argument type. + Infer the argument type. - By default, the argument `5` in `$ foo 5` becomes a string. Enabling this would infer it as a number. + By default, the argument `5` in `$ foo 5` becomes a string. Enabling this would infer it as a number. - @default false + @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`. - - _Note: If used in conjunction with `isMultiple`, the default flag value is set to `[]`._ - - __Caution: Explicitly specifying `undefined` for `booleanDefault` has different meaning from omitting key itself.__ - - @example - ``` - import meow from 'meow'; - - 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 - } + 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`. + + _Note: If used in conjunction with `isMultiple`, the default flag value is set to `[]`._ + + __Caution: Explicitly specifying `undefined` for `booleanDefault` has different meaning from omitting key itself.__ + + @example + ``` + import meow from 'meow'; + + 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 - // }, - // … - //} - ``` + } + }); + + //{ + // 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. + 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 + @default true */ readonly hardRejection?: boolean; /** - Whether to allow unknown flags or not. + Whether to allow unknown flags or not. - @default true + @default true */ readonly allowUnknownFlags?: boolean; } @@ -233,34 +233,34 @@ export type TypedFlags = { export interface Result { /** - Non-flag arguments. + Non-flag arguments. */ input: string[]; /** - Flags converted to camelCase excluding aliases. + Flags converted to camelCase excluding aliases. */ flags: TypedFlags & Record; /** - Flags converted camelCase including aliases. + Flags converted camelCase including aliases. */ unnormalizedFlags: TypedFlags & Record; /** - The `package.json` object. + The `package.json` object. */ pkg: PackageJson; /** - The help text used with `--help`. + The help text used with `--help`. */ help: string; /** - Show the help text and exit with code. + Show the help text and exit with code. - @param exitCode - The exit code to use. Default: `2`. + @param exitCode - The exit code to use. Default: `2`. */ showHelp: (exitCode?: number) => void; @@ -275,7 +275,7 @@ export interface Result { @example ``` #!/usr/bin/env node -import meow = from 'meow'; +import meow from 'meow'; import foo from './index.js'; const cli = meow(` From cea054ee609e30047f5d0120bbbdf4e6800a590a Mon Sep 17 00:00:00 2001 From: LitoMore Date: Sun, 25 Apr 2021 23:07:53 +0800 Subject: [PATCH 10/11] Use `export default` for overload functions --- index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index dec20b5..873971d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -307,6 +307,6 @@ foo(cli.input[0], cli.flags); ``` */ -declare function meow(helpMessage: string | Options, options?: Options): Result; - -export default meow; +export default function meow(helpMessage: string, options?: Options): Result; +// eslint-disable-next-line no-redeclare +export default function meow(options?: Options): Result; From fe291e0d9f022ec8539e3675ee18d118b073de92 Mon Sep 17 00:00:00 2001 From: LitoMore Date: Tue, 27 Apr 2021 18:41:03 +0800 Subject: [PATCH 11/11] Fix indent --- index.d.ts | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/index.d.ts b/index.d.ts index 873971d..ec5c613 100644 --- a/index.d.ts +++ b/index.d.ts @@ -9,7 +9,7 @@ Callback function to determine if a flag is required during runtime. @param input - Contains the non-flag arguments. @returns True if the flag is required, otherwise false. - */ +*/ export type IsRequiredPredicate = (flags: Readonly, input: readonly string[]) => boolean; export interface Flag { @@ -61,14 +61,14 @@ export interface Options { } } ``` - */ + */ readonly flags?: Flags; /** 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; /** @@ -79,42 +79,42 @@ export interface Options { 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. This option is only considered when there is only one argument in `process.argv`. - */ + */ 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. This option is only considered when there is only one argument in `process.argv`. - */ + */ readonly autoVersion?: boolean; /** `package.json` as an `Object`. Default: Closest `package.json` upwards. _You most likely don't need this option._ - */ + */ readonly pkg?: Record; /** Custom arguments object. @default process.argv.slice(2) - */ + */ readonly argv?: readonly string[]; /** @@ -123,7 +123,7 @@ export interface Options { By default, the argument `5` in `$ foo 5` becomes a string. Enabling this would infer it as a number. @default false - */ + */ readonly inferType?: boolean; /** @@ -191,21 +191,21 @@ export interface Options { // … //} ``` - */ + */ 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; /** Whether to allow unknown flags or not. @default true - */ + */ readonly allowUnknownFlags?: boolean; } @@ -234,39 +234,39 @@ export type TypedFlags = { export interface Result { /** Non-flag arguments. - */ + */ input: string[]; /** Flags converted to camelCase excluding aliases. - */ + */ flags: TypedFlags & Record; /** Flags converted camelCase including aliases. - */ + */ unnormalizedFlags: TypedFlags & Record; /** 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. - */ + Show the version text and exit. + */ showVersion: () => void; } /**