/** * Simple function that implements a spinner animation in the terminal. * It receives an AbortController as argument and return a promise that resolves when the AbortController is signaled. * @param {AbortController} abortController * @returns {Promise} */ export function spinnerAnimation(abortController) { const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] let frameIndex = 0 return new Promise(resolve => { const intervalId = setInterval(() => { process.stdout.write(`\r${frames[frameIndex++]}`) frameIndex %= frames.length }, 100) const onAbort = () => { clearInterval(intervalId) process.stdout.write('\r \r') // Clear spinner resolve() } if (abortController.signal.aborted) { onAbort() } else { abortController.signal.addEventListener('abort', onAbort) } }) } /** * Wrap a long running task with a spinner animation. * @template T * @param {Promise} promise * @returns {Promise} */ export async function spinTask(promise) { const spinnerControl = new AbortController() const [, result] = await Promise.all([ spinnerAnimation(spinnerControl), promise.finally(() => spinnerControl.abort('Task is over')), ]) return result }