Skip to content

Commit 47b3bcb

Browse files
committed
Require Node.js 20
1 parent 8f3909c commit 47b3bcb

File tree

11 files changed

+74
-164
lines changed

11 files changed

+74
-164
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
node-version:
13-
- 21
13+
- 24
1414
- 20
15-
- 18
1615
steps:
17-
- uses: actions/checkout@v4
18-
- uses: actions/setup-node@v4
16+
- uses: actions/checkout@v5
17+
- uses: actions/setup-node@v5
1918
with:
2019
node-version: ${{ matrix.node-version }}
2120
- run: npm install

package.json

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
},
1818
"sideEffects": false,
1919
"engines": {
20-
"node": ">=18"
20+
"node": ">=20"
2121
},
2222
"scripts": {
2323
"prepare": "npm run build",
@@ -60,39 +60,41 @@
6060
"yargs-parser"
6161
],
6262
"devDependencies": {
63-
"@rollup/plugin-commonjs": "^25.0.7",
63+
"@rollup/plugin-commonjs": "^28.0.6",
6464
"@rollup/plugin-json": "^6.1.0",
65-
"@rollup/plugin-node-resolve": "^15.2.3",
65+
"@rollup/plugin-node-resolve": "^16.0.1",
66+
"@sindresorhus/tsconfig": "^8.0.1",
6667
"@types/minimist": "^1.2.5",
67-
"ava": "^6.1.1",
68-
"camelcase-keys": "^9.1.3",
68+
"ava": "^6.4.1",
69+
"camelcase-keys": "^10.0.0",
6970
"common-tags": "^2.0.0-alpha.1",
70-
"decamelize": "^6.0.0",
71+
"decamelize": "^6.0.1",
7172
"decamelize-keys": "^2.0.1",
7273
"delete_comments": "^0.0.2",
73-
"execa": "^8.0.1",
74-
"globby": "^14.0.1",
74+
"execa": "^9.6.0",
75+
"globby": "^14.1.0",
7576
"indent-string": "^5.0.0",
7677
"minimist-options": "4.1.0",
77-
"normalize-package-data": "^6.0.0",
78+
"normalize-package-data": "^8.0.0",
7879
"read-package-up": "^11.0.0",
7980
"read-pkg": "^9.0.1",
8081
"redent": "^4.0.0",
81-
"rollup": "^4.12.0",
82-
"rollup-plugin-dts": "^6.1.0",
83-
"rollup-plugin-license": "^3.2.0",
82+
"rollup": "^4.50.2",
83+
"rollup-plugin-dts": "^6.2.3",
84+
"rollup-plugin-license": "^3.6.0",
8485
"stack-utils": "^2.0.6",
8586
"trim-newlines": "^5.0.0",
86-
"tsd": "^0.30.7",
87-
"type-fest": "^4.10.3",
88-
"typescript": "~5.3.3",
89-
"xo": "^0.57.0",
90-
"yargs-parser": "^21.1.1"
87+
"tsd": "^0.33.0",
88+
"type-fest": "^5.0.0",
89+
"typescript": "~5.9.2",
90+
"xo": "^1.2.2",
91+
"yargs-parser": "^22.0.0"
9192
},
9293
"xo": {
9394
"rules": {
9495
"unicorn/no-process-exit": "off",
95-
"unicorn/error-message": "off"
96+
"unicorn/error-message": "off",
97+
"@typescript-eslint/no-unsafe-call": "off"
9698
},
9799
"ignores": [
98100
"build"

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ Infer the argument type.
213213

214214
By default, the argument `5` in `$ foo 5` becomes a string. Enabling this would infer it as a number.
215215

216+
Also applies to flags without explicit types. By default, `--count 42` remains as the string `'42'`. With `inferType: true`, it becomes the number `42`.
217+
216218
##### booleanDefault
217219

218220
Type: `boolean | undefined`\

rollup.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,5 @@ const dtsConfig = defineConfig({
8484
],
8585
});
8686

87-
// eslint-disable-next-line import/no-anonymous-default-export
87+
// eslint-disable-next-line import-x/no-anonymous-default-export
8888
export default [config, dtsConfig];

source/index.d.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -291,15 +291,6 @@ export type Options<Flags extends AnyFlags> = {
291291
*/
292292
readonly booleanDefault?: boolean | undefined;
293293

294-
// TODO: Remove this in meow 14.
295-
/**
296-
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.
297-
298-
@deprecated This is the default behavior since Node.js 16, so this option is moot.
299-
@default true
300-
*/
301-
readonly hardRejection?: boolean;
302-
303294
/**
304295
Whether to allow unknown flags or not.
305296

source/options.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import process from 'node:process';
2-
import {dirname} from 'node:path';
2+
import path from 'node:path';
33
import {fileURLToPath} from 'node:url';
44
import {readPackageUpSync} from 'read-package-up';
55
import normalizePackageData from 'normalize-package-data';
@@ -65,11 +65,10 @@ export const buildOptions = (helpText, options) => {
6565
}
6666

6767
const foundPackage = options.pkg ?? readPackageUpSync({
68-
cwd: dirname(fileURLToPath(options.importMeta.url)),
68+
cwd: path.dirname(fileURLToPath(options.importMeta.url)),
6969
normalize: false,
7070
})?.packageJson;
7171

72-
// eslint-disable-next-line unicorn/prevent-abbreviations
7372
const pkg = foundPackage ?? {};
7473
normalizePackageData(pkg);
7574

test-d/index.ts

Lines changed: 15 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,64 @@
11
import {expectAssignable, expectError, expectType} from 'tsd';
22
import type {PackageJson} from 'type-fest';
3-
import meow, {type Result} from '../source/index.js';
3+
import meow from '../source/index.js';
44

55
type AnyFlag = NonNullable<NonNullable<Parameters<typeof meow>[0]>['flags']>[string];
66

77
const importMeta = import.meta;
88

9+
// Missing importMeta should error
910
expectError(meow('Help text'));
1011
expectError(meow('Help text', {}));
11-
expectType<Result<never>>(meow('Help text', {importMeta, hardRejection: false}));
12-
expectAssignable<{flags: {foo: number}}>(
13-
meow({importMeta: import.meta, flags: {foo: {type: 'number', isRequired: true}}}),
14-
);
15-
expectAssignable<{flags: {foo: string}}>(
16-
meow({importMeta, flags: {foo: {type: 'string', isRequired: true}}}),
17-
);
18-
expectAssignable<{flags: {foo: boolean}}>(
19-
meow({importMeta, flags: {foo: {type: 'boolean', isRequired: true}}}),
20-
);
21-
expectAssignable<{flags: {foo: number | undefined}}>(
22-
meow({importMeta, flags: {foo: {type: 'number'}}}),
23-
);
24-
expectAssignable<{flags: {foo: string | undefined}}>(
25-
meow({importMeta, flags: {foo: {type: 'string'}}}),
26-
);
27-
expectAssignable<{flags: {foo: boolean | undefined}}>(
28-
meow({importMeta, flags: {foo: {type: 'boolean'}}}),
29-
);
30-
expectAssignable<{flags: {foo: number[] | undefined}}>(
31-
meow({importMeta, flags: {foo: {type: 'number', isMultiple: true}}}),
32-
);
33-
expectAssignable<{flags: {foo: string[] | undefined}}>(
34-
meow({importMeta, flags: {foo: {type: 'string', isMultiple: true}}}),
35-
);
36-
expectAssignable<{flags: {foo: boolean[] | undefined}}>(
37-
meow({importMeta, flags: {foo: {type: 'boolean', isMultiple: true}}}),
38-
);
39-
expectType<Result<never>>(meow({importMeta, description: 'foo'}));
40-
expectType<Result<never>>(meow({importMeta, description: false}));
41-
expectType<Result<never>>(meow({importMeta, help: 'foo'}));
42-
expectType<Result<never>>(meow({importMeta, help: false}));
43-
expectType<Result<never>>(meow({importMeta, version: 'foo'}));
44-
expectType<Result<never>>(meow({importMeta, autoHelp: false}));
45-
expectType<Result<never>>(meow({importMeta, autoVersion: false}));
46-
expectType<Result<never>>(meow({importMeta, pkg: {foo: 'bar'}}));
47-
expectType<Result<never>>(meow({importMeta, argv: ['foo', 'bar']}));
48-
expectType<Result<never>>(meow({importMeta, inferType: true}));
49-
expectType<Result<never>>(meow({importMeta, booleanDefault: true}));
50-
expectType<Result<never>>(meow({importMeta, booleanDefault: undefined}));
51-
expectType<Result<never>>(meow({importMeta, hardRejection: false}));
5212

13+
// Flag types
14+
expectAssignable<{flags: {foo: number}}>(meow({importMeta, flags: {foo: {type: 'number', isRequired: true}}}));
15+
expectAssignable<{flags: {foo: string | undefined}}>(meow({importMeta, flags: {foo: {type: 'string'}}}));
16+
expectAssignable<{flags: {foo: boolean[] | undefined}}>(meow({importMeta, flags: {foo: {type: 'boolean', isMultiple: true}}}));
17+
18+
// Complete result test
5319
const result = meow('Help text', {
5420
importMeta,
5521
flags: {
5622
foo: {type: 'boolean', shortFlag: 'f'},
57-
'foo-bar': {type: 'number', aliases: ['foobar', 'fooBar']},
23+
'foo-bar': {type: 'number', aliases: ['foobar']},
5824
bar: {type: 'string', default: ''},
5925
abc: {type: 'string', isMultiple: true},
60-
baz: {type: 'string', choices: ['rainbow', 'cat', 'unicorn']},
26+
baz: {type: 'string', choices: ['rainbow', 'cat']},
6127
},
6228
});
6329

6430
expectType<string[]>(result.input);
6531
expectType<PackageJson>(result.pkg);
6632
expectType<string>(result.help);
67-
6833
expectType<boolean | undefined>(result.flags.foo);
6934
expectType<number | undefined>(result.flags.fooBar);
7035
expectType<string>(result.flags.bar);
7136
expectType<string[] | undefined>(result.flags.abc);
7237
expectType<string | undefined>(result.flags.baz);
73-
expectType<boolean | undefined>(result.unnormalizedFlags.foo);
74-
expectType<unknown>(result.unnormalizedFlags.f);
75-
expectType<number | undefined>(result.unnormalizedFlags['foo-bar']);
76-
expectType<unknown>(result.unnormalizedFlags.foobar);
77-
expectType<unknown>(result.unnormalizedFlags.fooBar);
78-
expectType<string>(result.unnormalizedFlags.bar);
79-
expectType<string[] | undefined>(result.unnormalizedFlags.abc);
80-
expectType<string | undefined>(result.unnormalizedFlags.baz);
8138

8239
result.showHelp();
8340
result.showHelp(1);
8441
result.showVersion();
8542

43+
// Const assertion
8644
const options = {
8745
importMeta,
8846
flags: {
89-
rainbow: {
90-
type: 'boolean',
91-
shortFlag: 'r',
92-
},
47+
rainbow: {type: 'boolean', shortFlag: 'r'},
9348
},
9449
} as const;
9550

9651
meow('', options);
9752

53+
// Flag validation - defaults
9854
expectAssignable<AnyFlag>({type: 'string', default: 'cat'});
9955
expectAssignable<AnyFlag>({type: 'number', default: 42});
100-
expectAssignable<AnyFlag>({type: 'boolean', default: true});
101-
102-
expectAssignable<AnyFlag>({type: 'string', default: undefined});
103-
expectAssignable<AnyFlag>({type: 'number', default: undefined});
104-
expectAssignable<AnyFlag>({type: 'boolean', default: undefined});
105-
10656
expectAssignable<AnyFlag>({type: 'string', isMultiple: true, default: ['cat']});
107-
expectAssignable<AnyFlag>({type: 'number', isMultiple: true, default: [42]});
108-
expectAssignable<AnyFlag>({type: 'boolean', isMultiple: true, default: [false]});
109-
11057
expectError<AnyFlag>({type: 'string', isMultiple: true, default: 'cat'});
111-
expectError<AnyFlag>({type: 'number', isMultiple: true, default: 42});
112-
expectError<AnyFlag>({type: 'boolean', isMultiple: true, default: false});
11358

59+
// Flag validation - choices
11460
expectAssignable<AnyFlag>({type: 'string', choices: ['cat', 'unicorn']});
115-
expectAssignable<AnyFlag>({type: 'number', choices: [1, 2]});
116-
expectAssignable<AnyFlag>({type: 'boolean', choices: [true, false]});
117-
expectAssignable<AnyFlag>({type: 'string', isMultiple: true, choices: ['cat']});
118-
expectAssignable<AnyFlag>({type: 'string', isMultiple: false, choices: ['cat']});
119-
61+
expectAssignable<AnyFlag>({choices: ['cat']});
12062
expectError<AnyFlag>({type: 'string', choices: 'cat'});
121-
expectError<AnyFlag>({type: 'number', choices: 1});
122-
expectError<AnyFlag>({type: 'boolean', choices: true});
123-
12463
expectError<AnyFlag>({type: 'string', choices: [1]});
125-
expectError<AnyFlag>({type: 'number', choices: ['cat']});
126-
expectError<AnyFlag>({type: 'boolean', choices: ['cat']});
127-
128-
expectAssignable<AnyFlag>({choices: ['cat']});
129-
expectAssignable<AnyFlag>({choices: [1]});
130-
expectAssignable<AnyFlag>({choices: [true]});
13164
expectError<AnyFlag>({choices: ['cat', 1, true]});

test/_utils.js

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,36 +35,34 @@ const stackUtils = new StackUtils();
3535

3636
export const stackToErrorMessage = stack => stackUtils.clean(stack).split('\n').at(0);
3737

38-
export const _verifyCli = (baseFixture = defaultFixture) => test.macro(
39-
async (t, {fixture = baseFixture, args, execaOptions, expected, error}) => {
40-
const assertions = await t.try(async tt => {
41-
const arguments_ = args ? args.split(' ') : [];
42-
const {all: output, exitCode} = await spawnFixture(fixture, arguments_, {reject: false, all: true, ...execaOptions});
43-
tt.log('args:', arguments_);
38+
export const _verifyCli = (baseFixture = defaultFixture) => test.macro(async (t, {fixture = baseFixture, args, execaOptions, expected, error}) => {
39+
const assertions = await t.try(async tt => {
40+
const arguments_ = args ? args.split(' ') : [];
41+
const {all: output, exitCode} = await spawnFixture(fixture, arguments_, {reject: false, all: true, ...execaOptions});
42+
tt.log('args:', arguments_);
4443

45-
if (error) {
46-
tt.log(`error (code ${exitCode}):\n`, output);
44+
if (error) {
45+
tt.log(`error (code ${exitCode}):\n`, output);
4746

48-
if (typeof error === 'string') {
49-
tt.is(output, error);
50-
tt.is(exitCode, 2);
51-
} else {
52-
const error_ = error.clean ? stackToErrorMessage(output) : output;
53-
54-
tt.is(error_, error.message);
55-
tt.is(exitCode, error.code);
56-
}
47+
if (typeof error === 'string') {
48+
tt.is(output, error);
49+
tt.is(exitCode, 2);
5750
} else {
58-
tt.log('output:\n', output);
51+
const error_ = error.clean ? stackToErrorMessage(output) : output;
5952

60-
if (expected) {
61-
tt.is(output, expected);
62-
} else {
63-
tt.pass();
64-
}
53+
tt.is(error_, error.message);
54+
tt.is(exitCode, error.code);
6555
}
66-
});
56+
} else {
57+
tt.log('output:\n', output);
6758

68-
assertions.commit({retainLogs: !assertions.passed});
69-
},
70-
);
59+
if (expected) {
60+
tt.is(output, expected);
61+
} else {
62+
tt.pass();
63+
}
64+
}
65+
});
66+
67+
assertions.commit({retainLogs: !assertions.passed});
68+
});

test/fixtures/required/fixture-required-function.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ const cli = meow({
2424
shouldError: {
2525
type: 'boolean',
2626
isRequired: (flags, _) =>
27-
flags.allowError ? 'should error' : false
28-
,
27+
flags.allowError ? 'should error' : false,
2928
},
3029
},
3130
});

test/options/pkg.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ test('description', t => {
2020
`);
2121
});
2222

23-
test.todo('version');
24-
2523
test('overriding pkg still normalizes', t => {
2624
const cli = meow({
2725
importMeta,
@@ -37,8 +35,6 @@ test('overriding pkg still normalizes', t => {
3735
version: '',
3836
},
3937
});
40-
41-
// TODO: test that showVersion logs undefined
4238
});
4339

4440
test('process title - bin default', verifyPackage, {

0 commit comments

Comments
 (0)