Skip to content

Commit

Permalink
Improve ora.promise() (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
SrBrahma committed Aug 23, 2021
1 parent d51c971 commit 9c01990
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 33 deletions.
28 changes: 22 additions & 6 deletions index.d.ts
Expand Up @@ -133,6 +133,22 @@ declare namespace ora {
readonly prefixText?: string | PrefixTextGenerator;
}

interface PromiseOptions<T> extends Options {
/**
The new text of the spinner when the promise is resolved.
If undefined, will keep the initial text.
*/
successText?: string | ((result: T) => string);

/**
The new text of the spinner when the promise is rejected.
If undefined, will keep the initial text.
*/
failText?: string | ((error: Error) => string);
}

interface Ora {
/**
A boolean of whether the instance is currently spinning.
Expand Down Expand Up @@ -259,19 +275,19 @@ declare const ora: {
}, 1000);
```
*/
(options?: ora.Options | string): ora.Ora;
(options?: string | ora.Options): ora.Ora;

/**
Starts a spinner for a promise. The spinner is stopped with `.succeed()` if the promise fulfills or with `.fail()` if it rejects.
Starts a spinner for a function or a promise. The spinner is stopped with `.succeed()` if the promise fulfills or with `.fail()` if it rejects. Returns the Promise.
@param action - The promise to start the spinner for.
@param options - If a string is provided, it is treated as a shortcut for `options.text`.
@returns The spinner instance.
*/
promise(
action: PromiseLike<unknown>,
options?: ora.Options | string
): ora.Ora;
promise<T>(
action: PromiseLike<T> | ((spinner: ora.Ora) => PromiseLike<T>),
options?: string | ora.PromiseOptions<T>
): Promise<T>;
};

export = ora;
42 changes: 27 additions & 15 deletions index.js
Expand Up @@ -393,23 +393,35 @@ const oraFactory = function (options) {

module.exports = oraFactory;

module.exports.promise = (action, options) => {
// https://github.com/sindresorhus/ora/issues/169#issuecomment-873269524
module.exports.promise = async (action, options) => {
const actionIsFunction = typeof action === 'function';
// eslint-disable-next-line promise/prefer-await-to-then
if (typeof action.then !== 'function') {
throw new TypeError('Parameter `action` must be a Promise');
}
const actionIsPromise = typeof action.then === 'function';

const spinner = new Ora(options);
spinner.start();
if (!actionIsFunction && !actionIsPromise) {
throw new TypeError('Parameter `action` must be a Function or a Promise');
}

(async () => {
try {
await action;
spinner.succeed();
} catch {
spinner.fail();
}
})();
const {successText, failText} = typeof options === 'object' ?
options :
{successText: undefined, failText: undefined};

return spinner;
const spinner = new Ora(options); // Set the initial string or ora options.
spinner.start();
try {
const promise = actionIsFunction ? action(spinner) : action;
const result = await promise;
spinner.succeed(successText === undefined ?
undefined :
(typeof successText === 'string' ? successText : successText(result))
);
return result;
} catch (error) {
spinner.fail(failText === undefined ?
undefined :
(typeof failText === 'string' ? failText : failText(error))
);
throw error;
}
};
20 changes: 18 additions & 2 deletions index.test-d.ts
Expand Up @@ -45,11 +45,27 @@ spinner.render();
spinner.frame();

const resolves = Promise.resolve(1);
promise(resolves, 'foo');
promise(resolves, {
void promise(resolves, 'foo');
void promise(resolves, {
stream: new PassThroughStream(),
text: 'foo',
color: 'blue',
isEnabled: true,
isSilent: false
});
void promise(async () => {
await resolves;
}, 'foo');
void promise(async spinner => {
spinner.prefixText = 'foo';
await resolves;
return 7;
}, {
stream: new PassThroughStream(),
text: 'foo',
color: 'blue',
isEnabled: true,
isSilent: false,
successText: result => `Resolved with number ${result}`,
failText: 'bar'
});
21 changes: 19 additions & 2 deletions readme.md
Expand Up @@ -226,11 +226,28 @@ Change the spinner indent.
### ora.promise(action, text)
### ora.promise(action, options)

Starts a spinner for a promise. The spinner is stopped with `.succeed()` if the promise fulfills or with `.fail()` if it rejects. Returns the spinner instance.
Starts a spinner for a function or a promise. The spinner is stopped with `.succeed()` if the promise fulfills or with `.fail()` if it rejects. Returns the promise.

#### action

Type: `Promise`
Type: `Promise | ((spinner: ora.Ora) => Promise)`

#### options

Type: `object`

All of the [options](#options) plus the following:

##### successText
Type: `string | ((result: T) => string)`

The new text of the spinner when the promise is resolved. If undefined, will keep the initial text.

##### failText
Type: `string | ((error: Error) => string)`

The new text of the spinner when the promise is rejected. If undefined, will keep the initial text.


## FAQ

Expand Down
14 changes: 6 additions & 8 deletions test.js
Expand Up @@ -169,15 +169,13 @@ test('.promise() - rejects', async t => {
const output = getStream(stream);
const rejects = Promise.reject(new Error()); // eslint-disable-line unicorn/error-message

Ora.promise(rejects, {
stream,
text: 'foo',
color: false,
isEnabled: true
});

try {
await rejects;
await Ora.promise(rejects, {
stream,
text: 'foo',
color: false,
isEnabled: true
})
} catch {}

stream.end();
Expand Down

0 comments on commit 9c01990

Please sign in to comment.