diff --git a/docs/api.md b/docs/api.md index 9bcf3e2cb..0bd5d9e16 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1338,9 +1338,21 @@ the resulting error and output will not be passed to the `parse()` callback (the ***Note:*** `parse()` should be called only once when [`command()`](#command) is called with a handler returning a promise. If your use case requires `parse()` to be called several times, any asynchronous -operation performed in a command handler should not result in the handler returning a promise +operation performed in a command handler should not result in the handler returning a promise. -.parsed +.parseAsync([args], [context], [parseCallback]) +------------ + +Identical to `.parse()` except always returns a promise for a parsed argv +object, regardless of whether an async builder, handler, or middleware is used. + +.parseSync([args], [context], [parseCallback]) +------------ + +Identical to `.parse()` except an exception is thrown if an asynchronous +builder, handler, or middleware is used. + +.parsed [DEPRECATED] ------------ If the arguments have not been parsed, this property is `false`. diff --git a/lib/yargs-factory.ts b/lib/yargs-factory.ts index 54c1c3ba8..5ef10e8f3 100644 --- a/lib/yargs-factory.ts +++ b/lib/yargs-factory.ts @@ -1082,6 +1082,31 @@ export class YargsInstance { } return parsed; } + parseAsync( + args?: string | string[], + shortCircuit?: object | ParseCallback | boolean, + _parseFn?: ParseCallback + ): Promise { + const maybePromise = this.parse(args, shortCircuit, _parseFn); + if (!isPromise(maybePromise)) { + return Promise.resolve(maybePromise); + } else { + return maybePromise; + } + } + parseSync( + args?: string | string[], + shortCircuit?: object | ParseCallback | boolean, + _parseFn?: ParseCallback + ): Arguments { + const maybePromise = this.parse(args, shortCircuit, _parseFn); + if (isPromise(maybePromise)) { + throw new YError( + '.parseSync() must not be used with asynchronous builders, handlers, or middleware' + ); + } + return maybePromise; + } parserConfiguration(config: Configuration) { argsert('', [config], arguments.length); this.#parserConfig = config; diff --git a/test/yargs.cjs b/test/yargs.cjs index 1840fda46..7b81729f0 100644 --- a/test/yargs.cjs +++ b/test/yargs.cjs @@ -23,6 +23,9 @@ function clearRequireCache() { delete require.cache[require.resolve('../index.cjs')]; delete require.cache[require.resolve('../build/index.cjs')]; } +function isPromise(maybePromise) { + return typeof maybePromise.then === 'function'; +} describe('yargs dsl tests', () => { const oldProcess = {versions: {}}; @@ -3115,4 +3118,50 @@ describe('yargs dsl tests', () => { assert.strictEqual(y2.getStrictOptions(), false); }); }); + describe('parseAsync', () => { + it('returns promise when parse is synchronous', () => { + const argv = yargs('foo').parseAsync(); + assert.strictEqual(isPromise(argv), true); + }); + it('returns promise when parse is asynchronous', async () => { + const argv = yargs('--foo bar') + .middleware(async () => { + await wait(); + }) + .parseAsync(); + assert.strictEqual(isPromise(argv), true); + assert.strictEqual((await argv).foo, 'bar'); + }); + }); + describe('parseSync', () => { + it('succeeds if no async functions used during parsing', () => { + const argv = yargs('foo 33') + .command( + 'foo [bar]', + 'foo command', + () => {}, + () => {} + ) + .middleware(argv => { + argv.bar *= 2; + }) + .parseSync(); + assert.strictEqual(argv.bar, 66); + }); + it('throws if any async method is used', () => { + assert.throws(() => { + yargs('foo 33') + .command( + 'foo [bar]', + 'foo command', + () => {}, + () => {} + ) + .middleware(async argv => { + argv.bar *= 2; + }) + .parseSync(); + }, /.*parseSync\(\) must not be used.*/); + }); + }); });