Comparing version
463
index.d.ts
@@ -1,277 +0,332 @@ | ||
import {SpinnerName} from 'cli-spinners'; | ||
import {type SpinnerName} from 'cli-spinners'; | ||
declare namespace ora { | ||
interface Spinner { | ||
readonly interval?: number; | ||
readonly frames: string[]; | ||
} | ||
export type Spinner = { | ||
readonly interval?: number; | ||
readonly frames: string[]; | ||
}; | ||
type Color = | ||
| 'black' | ||
| 'red' | ||
| 'green' | ||
| 'yellow' | ||
| 'blue' | ||
| 'magenta' | ||
| 'cyan' | ||
| 'white' | ||
| 'gray'; | ||
export type Color = | ||
| 'black' | ||
| 'red' | ||
| 'green' | ||
| 'yellow' | ||
| 'blue' | ||
| 'magenta' | ||
| 'cyan' | ||
| 'white' | ||
| 'gray'; | ||
type PrefixTextGenerator = () => string; | ||
export type PrefixTextGenerator = () => string; | ||
interface Options { | ||
/** | ||
Text to display after the spinner. | ||
*/ | ||
readonly text?: string; | ||
export type SuffixTextGenerator = () => string; | ||
/** | ||
Text or a function that returns text to display before the spinner. No prefix text will be displayed if set to an empty string. | ||
*/ | ||
readonly prefixText?: string | PrefixTextGenerator; | ||
export type Options = { | ||
/** | ||
Text to display after the spinner. | ||
*/ | ||
readonly text?: string; | ||
/** | ||
Name of one of the provided spinners. See [`example.js`](https://github.com/BendingBender/ora/blob/main/example.js) in this repo if you want to test out different spinners. On Windows, it will always use the line spinner as the Windows command-line doesn't have proper Unicode support. | ||
/** | ||
Text or a function that returns text to display before the spinner. No prefix text will be displayed if set to an empty string. | ||
*/ | ||
readonly prefixText?: string | PrefixTextGenerator; | ||
@default 'dots' | ||
/** | ||
Text or a function that returns text to display after the spinner text. No suffix text will be displayed if set to an empty string. | ||
*/ | ||
readonly suffixText?: string | SuffixTextGenerator; | ||
Or an object like: | ||
/** | ||
Name of one of the provided spinners. See [`example.js`](https://github.com/BendingBender/ora/blob/main/example.js) in this repo if you want to test out different spinners. On Windows, it will always use the line spinner as the Windows command-line doesn't have proper Unicode support. | ||
@example | ||
``` | ||
{ | ||
interval: 80, // Optional | ||
frames: ['-', '+', '-'] | ||
} | ||
``` | ||
*/ | ||
readonly spinner?: SpinnerName | Spinner; | ||
@default 'dots' | ||
/** | ||
Color of the spinner. | ||
Or an object like: | ||
@default 'cyan' | ||
*/ | ||
readonly color?: Color; | ||
@example | ||
``` | ||
{ | ||
interval: 80, // Optional | ||
frames: ['-', '+', '-'] | ||
} | ||
``` | ||
*/ | ||
readonly spinner?: SpinnerName | Spinner; | ||
/** | ||
Set to `false` to stop Ora from hiding the cursor. | ||
/** | ||
The color of the spinner. | ||
@default true | ||
*/ | ||
readonly hideCursor?: boolean; | ||
@default 'cyan' | ||
*/ | ||
readonly color?: Color; | ||
/** | ||
Indent the spinner with the given number of spaces. | ||
/** | ||
Set to `false` to stop Ora from hiding the cursor. | ||
@default 0 | ||
*/ | ||
readonly indent?: number; | ||
@default true | ||
*/ | ||
readonly hideCursor?: boolean; | ||
/** | ||
Interval between each frame. | ||
/** | ||
Indent the spinner with the given number of spaces. | ||
Spinners provide their own recommended interval, so you don't really need to specify this. | ||
@default 0 | ||
*/ | ||
readonly indent?: number; | ||
Default: Provided by the spinner or `100`. | ||
*/ | ||
readonly interval?: number; | ||
/** | ||
Interval between each frame. | ||
/** | ||
Stream to write the output. | ||
Spinners provide their own recommended interval, so you don't really need to specify this. | ||
You could for example set this to `process.stdout` instead. | ||
Default: Provided by the spinner or `100`. | ||
*/ | ||
readonly interval?: number; | ||
@default process.stderr | ||
*/ | ||
readonly stream?: NodeJS.WritableStream; | ||
/** | ||
Stream to write the output. | ||
/** | ||
Force enable/disable the spinner. If not specified, the spinner will be enabled if the `stream` is being run inside a TTY context (not spawned or piped) and/or not in a CI environment. | ||
You could for example set this to `process.stdout` instead. | ||
Note that `{isEnabled: false}` doesn't mean it won't output anything. It just means it won't output the spinner, colors, and other ansi escape codes. It will still log text. | ||
*/ | ||
readonly isEnabled?: boolean; | ||
@default process.stderr | ||
*/ | ||
readonly stream?: NodeJS.WritableStream; | ||
/** | ||
Disable the spinner and all log text. All output is suppressed and `isEnabled` will be considered `false`. | ||
/** | ||
Force enable/disable the spinner. If not specified, the spinner will be enabled if the `stream` is being run inside a TTY context (not spawned or piped) and/or not in a CI environment. | ||
@default false | ||
*/ | ||
readonly isSilent?: boolean; | ||
Note that `{isEnabled: false}` doesn't mean it won't output anything. It just means it won't output the spinner, colors, and other ansi escape codes. It will still log text. | ||
*/ | ||
readonly isEnabled?: boolean; | ||
/** | ||
Discard stdin input (except Ctrl+C) while running if it's TTY. This prevents the spinner from twitching on input, outputting broken lines on `Enter` key presses, and prevents buffering of input while the spinner is running. | ||
/** | ||
Disable the spinner and all log text. All output is suppressed and `isEnabled` will be considered `false`. | ||
This has no effect on Windows as there's no good way to implement discarding stdin properly there. | ||
@default false | ||
*/ | ||
readonly isSilent?: boolean; | ||
@default true | ||
*/ | ||
readonly discardStdin?: boolean; | ||
} | ||
/** | ||
Discard stdin input (except Ctrl+C) while running if it's TTY. This prevents the spinner from twitching on input, outputting broken lines on `Enter` key presses, and prevents buffering of input while the spinner is running. | ||
interface PersistOptions { | ||
/** | ||
Symbol to replace the spinner with. | ||
This has no effect on Windows as there's no good way to implement discarding stdin properly there. | ||
@default ' ' | ||
*/ | ||
readonly symbol?: string; | ||
@default true | ||
*/ | ||
readonly discardStdin?: boolean; | ||
}; | ||
/** | ||
Text to be persisted after the symbol. | ||
export type PersistOptions = { | ||
/** | ||
Symbol to replace the spinner with. | ||
Default: Current `text`. | ||
*/ | ||
readonly text?: string; | ||
@default ' ' | ||
*/ | ||
readonly symbol?: string; | ||
/** | ||
Text or a function that returns text to be persisted before the symbol. No prefix text will be displayed if set to an empty string. | ||
/** | ||
Text to be persisted after the symbol. | ||
Default: Current `prefixText`. | ||
*/ | ||
readonly prefixText?: string | PrefixTextGenerator; | ||
} | ||
Default: Current `text`. | ||
*/ | ||
readonly text?: string; | ||
interface Ora { | ||
/** | ||
A boolean of whether the instance is currently spinning. | ||
*/ | ||
readonly isSpinning: boolean; | ||
/** | ||
Text or a function that returns text to be persisted before the symbol. No prefix text will be displayed if set to an empty string. | ||
/** | ||
Change the text after the spinner. | ||
*/ | ||
text: string; | ||
Default: Current `prefixText`. | ||
*/ | ||
readonly prefixText?: string | PrefixTextGenerator; | ||
/** | ||
Change the text or function that returns text before the spinner. No prefix text will be displayed if set to an empty string. | ||
*/ | ||
prefixText: string | PrefixTextGenerator; | ||
/** | ||
Text or a function that returns text to be persisted after the text after the symbol. No suffix text will be displayed if set to an empty string. | ||
/** | ||
Change the spinner color. | ||
*/ | ||
color: Color; | ||
Default: Current `suffixText`. | ||
*/ | ||
readonly suffixText?: string | SuffixTextGenerator; | ||
}; | ||
/** | ||
Change the spinner. | ||
*/ | ||
spinner: SpinnerName | Spinner; | ||
export type PromiseOptions<T> = { | ||
/** | ||
The new text of the spinner when the promise is resolved. | ||
/** | ||
Change the spinner indent. | ||
*/ | ||
indent: number; | ||
Keeps the existing text if `undefined`. | ||
*/ | ||
successText?: string | ((result: T) => string) | undefined; | ||
/** | ||
Start the spinner. | ||
/** | ||
The new text of the spinner when the promise is rejected. | ||
@param text - Set the current text. | ||
@returns The spinner instance. | ||
*/ | ||
start(text?: string): Ora; | ||
Keeps the existing text if `undefined`. | ||
*/ | ||
failText?: string | ((error: Error) => string) | undefined; | ||
} & Options; | ||
/** | ||
Stop and clear the spinner. | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions | ||
export interface Ora { | ||
/** | ||
Change the text after the spinner. | ||
*/ | ||
text: string; | ||
@returns The spinner instance. | ||
*/ | ||
stop(): Ora; | ||
/** | ||
Change the text or function that returns text before the spinner. | ||
/** | ||
Stop the spinner, change it to a green `✔` and persist the current text, or `text` if provided. | ||
No prefix text will be displayed if set to an empty string. | ||
*/ | ||
prefixText: string; | ||
@param text - Will persist text if provided. | ||
@returns The spinner instance. | ||
*/ | ||
succeed(text?: string): Ora; | ||
/** | ||
Change the text or function that returns text after the spinner text. | ||
/** | ||
Stop the spinner, change it to a red `✖` and persist the current text, or `text` if provided. | ||
No suffix text will be displayed if set to an empty string. | ||
*/ | ||
suffixText: string; | ||
@param text - Will persist text if provided. | ||
@returns The spinner instance. | ||
*/ | ||
fail(text?: string): Ora; | ||
/** | ||
Change the spinner color. | ||
*/ | ||
color: Color; | ||
/** | ||
Stop the spinner, change it to a yellow `⚠` and persist the current text, or `text` if provided. | ||
/** | ||
Change the spinner indent. | ||
*/ | ||
indent: number; | ||
@param text - Will persist text if provided. | ||
@returns The spinner instance. | ||
*/ | ||
warn(text?: string): Ora; | ||
/** | ||
Get the spinner. | ||
*/ | ||
get spinner(): Spinner; | ||
/** | ||
Stop the spinner, change it to a blue `ℹ` and persist the current text, or `text` if provided. | ||
/** | ||
Set the spinner. | ||
*/ | ||
set spinner(spinner: SpinnerName | Spinner); | ||
@param text - Will persist text if provided. | ||
@returns The spinner instance. | ||
*/ | ||
info(text?: string): Ora; | ||
/** | ||
A boolean of whether the instance is currently spinning. | ||
*/ | ||
get isSpinning(): boolean; | ||
/** | ||
Stop the spinner and change the symbol or text. | ||
/** | ||
The interval between each frame. | ||
@returns The spinner instance. | ||
*/ | ||
stopAndPersist(options?: PersistOptions): Ora; | ||
The interval is decided by the chosen spinner. | ||
*/ | ||
get interval(): number; | ||
/** | ||
Clear the spinner. | ||
/** | ||
Start the spinner. | ||
@returns The spinner instance. | ||
*/ | ||
clear(): Ora; | ||
@param text - Set the current text. | ||
@returns The spinner instance. | ||
*/ | ||
start(text?: string): this; | ||
/** | ||
Manually render a new frame. | ||
/** | ||
Stop and clear the spinner. | ||
@returns The spinner instance. | ||
*/ | ||
render(): Ora; | ||
@returns The spinner instance. | ||
*/ | ||
stop(): this; | ||
/** | ||
Get a new frame. | ||
/** | ||
Stop the spinner, change it to a green `✔` and persist the current text, or `text` if provided. | ||
@returns The spinner instance text. | ||
*/ | ||
frame(): string; | ||
} | ||
} | ||
@param text - Will persist text if provided. | ||
@returns The spinner instance. | ||
*/ | ||
succeed(text?: string): this; | ||
declare const ora: { | ||
/** | ||
Elegant terminal spinner. | ||
Stop the spinner, change it to a red `✖` and persist the current text, or `text` if provided. | ||
@param options - If a string is provided, it is treated as a shortcut for `options.text`. | ||
@param text - Will persist text if provided. | ||
@returns The spinner instance. | ||
*/ | ||
fail(text?: string): this; | ||
@example | ||
``` | ||
import ora = require('ora'); | ||
/** | ||
Stop the spinner, change it to a yellow `⚠` and persist the current text, or `text` if provided. | ||
const spinner = ora('Loading unicorns').start(); | ||
@param text - Will persist text if provided. | ||
@returns The spinner instance. | ||
*/ | ||
warn(text?: string): this; | ||
setTimeout(() => { | ||
spinner.color = 'yellow'; | ||
spinner.text = 'Loading rainbows'; | ||
}, 1000); | ||
``` | ||
/** | ||
Stop the spinner, change it to a blue `ℹ` and persist the current text, or `text` if provided. | ||
@param text - Will persist text if provided. | ||
@returns The spinner instance. | ||
*/ | ||
(options?: ora.Options | string): ora.Ora; | ||
info(text?: string): this; | ||
/** | ||
Starts a spinner for a promise. The spinner is stopped with `.succeed()` if the promise fulfills or with `.fail()` if it rejects. | ||
Stop the spinner and change the symbol or text. | ||
@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; | ||
}; | ||
stopAndPersist(options?: PersistOptions): this; | ||
export = ora; | ||
/** | ||
Clear the spinner. | ||
@returns The spinner instance. | ||
*/ | ||
clear(): this; | ||
/** | ||
Manually render a new frame. | ||
@returns The spinner instance. | ||
*/ | ||
render(): this; | ||
/** | ||
Get a new frame. | ||
@returns The spinner instance text. | ||
*/ | ||
frame(): string; | ||
} | ||
/** | ||
Elegant terminal spinner. | ||
@param options - If a string is provided, it is treated as a shortcut for `options.text`. | ||
@example | ||
``` | ||
import ora from 'ora'; | ||
const spinner = ora('Loading unicorns').start(); | ||
setTimeout(() => { | ||
spinner.color = 'yellow'; | ||
spinner.text = 'Loading rainbows'; | ||
}, 1000); | ||
``` | ||
*/ | ||
export default function ora(options?: string | Options): Ora; | ||
/** | ||
Starts a spinner for a promise or promise-returning function. The spinner is stopped with `.succeed()` if the promise fulfills or with `.fail()` if it rejects. | ||
@param action - The promise to start the spinner for or a promise-returning function. | ||
@param options - If a string is provided, it is treated as a shortcut for `options.text`. | ||
@returns The given promise. | ||
@example | ||
``` | ||
import {oraPromise} from 'ora'; | ||
await oraPromise(somePromise); | ||
``` | ||
*/ | ||
export function oraPromise<T>( | ||
action: PromiseLike<T> | ((spinner: Ora) => PromiseLike<T>), | ||
options?: string | PromiseOptions<T> | ||
): Promise<T>; | ||
export {default as spinners} from 'cli-spinners'; |
419
index.js
@@ -1,137 +0,93 @@ | ||
'use strict'; | ||
const readline = require('readline'); | ||
const chalk = require('chalk'); | ||
const cliCursor = require('cli-cursor'); | ||
const cliSpinners = require('cli-spinners'); | ||
const logSymbols = require('log-symbols'); | ||
const stripAnsi = require('strip-ansi'); | ||
const wcwidth = require('wcwidth'); | ||
const isInteractive = require('is-interactive'); | ||
const isUnicodeSupported = require('is-unicode-supported'); | ||
const {BufferListStream} = require('bl'); | ||
import process from 'node:process'; | ||
import chalk from 'chalk'; | ||
import cliCursor from 'cli-cursor'; | ||
import cliSpinners from 'cli-spinners'; | ||
import logSymbols from 'log-symbols'; | ||
import stripAnsi from 'strip-ansi'; | ||
import stringWidth from 'string-width'; | ||
import isInteractive from 'is-interactive'; | ||
import isUnicodeSupported from 'is-unicode-supported'; | ||
import stdinDiscarder from 'stdin-discarder'; | ||
const TEXT = Symbol('text'); | ||
const PREFIX_TEXT = Symbol('prefixText'); | ||
const ASCII_ETX_CODE = 0x03; // Ctrl+C emits this code | ||
class Ora { | ||
#linesToClear = 0; | ||
#isDiscardingStdin = false; | ||
#lineCount = 0; | ||
#frameIndex = 0; | ||
#options; | ||
#spinner; | ||
#stream; | ||
#id; | ||
#initialInterval; | ||
#isEnabled; | ||
#isSilent; | ||
#indent; | ||
#text; | ||
#prefixText; | ||
#suffixText; | ||
class StdinDiscarder { | ||
constructor() { | ||
this.requests = 0; | ||
color; | ||
this.mutedStream = new BufferListStream(); | ||
this.mutedStream.pipe(process.stdout); | ||
const self = this; // eslint-disable-line unicorn/no-this-assignment | ||
this.ourEmit = function (event, data, ...args) { | ||
const {stdin} = process; | ||
if (self.requests > 0 || stdin.emit === self.ourEmit) { | ||
if (event === 'keypress') { // Fixes readline behavior | ||
return; | ||
} | ||
if (event === 'data' && data.includes(ASCII_ETX_CODE)) { | ||
process.emit('SIGINT'); | ||
} | ||
Reflect.apply(self.oldEmit, this, [event, data, ...args]); | ||
} else { | ||
Reflect.apply(process.stdin.emit, this, [event, data, ...args]); | ||
} | ||
}; | ||
} | ||
start() { | ||
this.requests++; | ||
if (this.requests === 1) { | ||
this.realStart(); | ||
} | ||
} | ||
stop() { | ||
if (this.requests <= 0) { | ||
throw new Error('`stop` called more times than `start`'); | ||
} | ||
this.requests--; | ||
if (this.requests === 0) { | ||
this.realStop(); | ||
} | ||
} | ||
realStart() { | ||
// No known way to make it work reliably on Windows | ||
if (process.platform === 'win32') { | ||
return; | ||
} | ||
this.rl = readline.createInterface({ | ||
input: process.stdin, | ||
output: this.mutedStream | ||
}); | ||
this.rl.on('SIGINT', () => { | ||
if (process.listenerCount('SIGINT') === 0) { | ||
process.emit('SIGINT'); | ||
} else { | ||
this.rl.close(); | ||
process.kill(process.pid, 'SIGINT'); | ||
} | ||
}); | ||
} | ||
realStop() { | ||
if (process.platform === 'win32') { | ||
return; | ||
} | ||
this.rl.close(); | ||
this.rl = undefined; | ||
} | ||
} | ||
let stdinDiscarder; | ||
class Ora { | ||
constructor(options) { | ||
if (!stdinDiscarder) { | ||
stdinDiscarder = new StdinDiscarder(); | ||
} | ||
if (typeof options === 'string') { | ||
options = { | ||
text: options | ||
text: options, | ||
}; | ||
} | ||
this.options = { | ||
text: '', | ||
this.#options = { | ||
color: 'cyan', | ||
stream: process.stderr, | ||
discardStdin: true, | ||
...options | ||
hideCursor: true, | ||
...options, | ||
}; | ||
this.spinner = this.options.spinner; | ||
// Public | ||
this.color = this.#options.color; | ||
this.color = this.options.color; | ||
this.hideCursor = this.options.hideCursor !== false; | ||
this.interval = this.options.interval || this.spinner.interval || 100; | ||
this.stream = this.options.stream; | ||
this.id = undefined; | ||
this.isEnabled = typeof this.options.isEnabled === 'boolean' ? this.options.isEnabled : isInteractive({stream: this.stream}); | ||
this.isSilent = typeof this.options.isSilent === 'boolean' ? this.options.isSilent : false; | ||
// It's important that these use the public setters. | ||
this.spinner = this.#options.spinner; | ||
// Set *after* `this.stream` | ||
this.text = this.options.text; | ||
this.prefixText = this.options.prefixText; | ||
this.linesToClear = 0; | ||
this.indent = this.options.indent; | ||
this.discardStdin = this.options.discardStdin; | ||
this.isDiscardingStdin = false; | ||
this.#initialInterval = this.#options.interval; | ||
this.#stream = this.#options.stream; | ||
this.#isEnabled = typeof this.#options.isEnabled === 'boolean' ? this.#options.isEnabled : isInteractive({stream: this.#stream}); | ||
this.#isSilent = typeof this.#options.isSilent === 'boolean' ? this.#options.isSilent : false; | ||
// Set *after* `this.#stream`. | ||
// It's important that these use the public setters. | ||
this.text = this.#options.text; | ||
this.prefixText = this.#options.prefixText; | ||
this.suffixText = this.#options.suffixText; | ||
this.indent = this.#options.indent; | ||
if (process.env.NODE_ENV === 'test') { | ||
this._stream = this.#stream; | ||
this._isEnabled = this.#isEnabled; | ||
Object.defineProperty(this, '_linesToClear', { | ||
get() { | ||
return this.#linesToClear; | ||
}, | ||
set(newValue) { | ||
this.#linesToClear = newValue; | ||
}, | ||
}); | ||
Object.defineProperty(this, '_frameIndex', { | ||
get() { | ||
return this.#frameIndex; | ||
}, | ||
}); | ||
Object.defineProperty(this, '_lineCount', { | ||
get() { | ||
return this.#lineCount; | ||
}, | ||
}); | ||
} | ||
} | ||
get indent() { | ||
return this._indent; | ||
return this.#indent; | ||
} | ||
@@ -144,17 +100,17 @@ | ||
this._indent = indent; | ||
this.#indent = indent; | ||
this.#updateLineCount(); | ||
} | ||
_updateInterval(interval) { | ||
if (interval !== undefined) { | ||
this.interval = interval; | ||
} | ||
get interval() { | ||
return this.#initialInterval ?? this.#spinner.interval ?? 100; | ||
} | ||
get spinner() { | ||
return this._spinner; | ||
return this.#spinner; | ||
} | ||
set spinner(spinner) { | ||
this.frameIndex = 0; | ||
this.#frameIndex = 0; | ||
this.#initialInterval = undefined; | ||
@@ -166,41 +122,48 @@ if (typeof spinner === 'object') { | ||
this._spinner = spinner; | ||
this.#spinner = spinner; | ||
} else if (!isUnicodeSupported()) { | ||
this._spinner = cliSpinners.line; | ||
this.#spinner = cliSpinners.line; | ||
} else if (spinner === undefined) { | ||
// Set default spinner | ||
this._spinner = cliSpinners.dots; | ||
this.#spinner = cliSpinners.dots; | ||
} else if (spinner !== 'default' && cliSpinners[spinner]) { | ||
this._spinner = cliSpinners[spinner]; | ||
this.#spinner = cliSpinners[spinner]; | ||
} else { | ||
throw new Error(`There is no built-in spinner named '${spinner}'. See https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json for a full list.`); | ||
} | ||
this._updateInterval(this._spinner.interval); | ||
} | ||
get text() { | ||
return this[TEXT]; | ||
return this.#text; | ||
} | ||
set text(value) { | ||
this[TEXT] = value; | ||
this.updateLineCount(); | ||
set text(value = '') { | ||
this.#text = value; | ||
this.#updateLineCount(); | ||
} | ||
get prefixText() { | ||
return this[PREFIX_TEXT]; | ||
return this.#prefixText; | ||
} | ||
set prefixText(value) { | ||
this[PREFIX_TEXT] = value; | ||
this.updateLineCount(); | ||
set prefixText(value = '') { | ||
this.#prefixText = value; | ||
this.#updateLineCount(); | ||
} | ||
get suffixText() { | ||
return this.#suffixText; | ||
} | ||
set suffixText(value = '') { | ||
this.#suffixText = value; | ||
this.#updateLineCount(); | ||
} | ||
get isSpinning() { | ||
return this.id !== undefined; | ||
return this.#id !== undefined; | ||
} | ||
getFullPrefixText(prefixText = this[PREFIX_TEXT], postfix = ' ') { | ||
if (typeof prefixText === 'string') { | ||
#getFullPrefixText(prefixText = this.#prefixText, postfix = ' ') { | ||
if (typeof prefixText === 'string' && prefixText !== '') { | ||
return prefixText + postfix; | ||
@@ -216,13 +179,28 @@ } | ||
updateLineCount() { | ||
const columns = this.stream.columns || 80; | ||
const fullPrefixText = this.getFullPrefixText(this.prefixText, '-'); | ||
this.lineCount = 0; | ||
for (const line of stripAnsi(fullPrefixText + '--' + this[TEXT]).split('\n')) { | ||
this.lineCount += Math.max(1, Math.ceil(wcwidth(line) / columns)); | ||
#getFullSuffixText(suffixText = this.#suffixText, prefix = ' ') { | ||
if (typeof suffixText === 'string' && suffixText !== '') { | ||
return prefix + suffixText; | ||
} | ||
if (typeof suffixText === 'function') { | ||
return prefix + suffixText(); | ||
} | ||
return ''; | ||
} | ||
#updateLineCount() { | ||
const columns = this.#stream.columns ?? 80; | ||
const fullPrefixText = this.#getFullPrefixText(this.#prefixText, '-'); | ||
const fullSuffixText = this.#getFullSuffixText(this.#suffixText, '-'); | ||
const fullText = ' '.repeat(this.#indent) + fullPrefixText + '--' + this.#text + '--' + fullSuffixText; | ||
this.#lineCount = 0; | ||
for (const line of stripAnsi(fullText).split('\n')) { | ||
this.#lineCount += Math.max(1, Math.ceil(stringWidth(line, {countAnsiEscapeCodes: true}) / columns)); | ||
} | ||
} | ||
get isEnabled() { | ||
return this._isEnabled && !this.isSilent; | ||
return this.#isEnabled && !this.#isSilent; | ||
} | ||
@@ -235,7 +213,7 @@ | ||
this._isEnabled = value; | ||
this.#isEnabled = value; | ||
} | ||
get isSilent() { | ||
return this._isSilent; | ||
return this.#isSilent; | ||
} | ||
@@ -248,8 +226,8 @@ | ||
this._isSilent = value; | ||
this.#isSilent = value; | ||
} | ||
frame() { | ||
const {frames} = this.spinner; | ||
let frame = frames[this.frameIndex]; | ||
const {frames} = this.#spinner; | ||
let frame = frames[this.#frameIndex]; | ||
@@ -260,25 +238,32 @@ if (this.color) { | ||
this.frameIndex = ++this.frameIndex % frames.length; | ||
const fullPrefixText = (typeof this.prefixText === 'string' && this.prefixText !== '') ? this.prefixText + ' ' : ''; | ||
this.#frameIndex = ++this.#frameIndex % frames.length; | ||
const fullPrefixText = (typeof this.#prefixText === 'string' && this.#prefixText !== '') ? this.#prefixText + ' ' : ''; | ||
const fullText = typeof this.text === 'string' ? ' ' + this.text : ''; | ||
const fullSuffixText = (typeof this.#suffixText === 'string' && this.#suffixText !== '') ? ' ' + this.#suffixText : ''; | ||
return fullPrefixText + frame + fullText; | ||
return fullPrefixText + frame + fullText + fullSuffixText; | ||
} | ||
clear() { | ||
if (!this.isEnabled || !this.stream.isTTY) { | ||
if (!this.#isEnabled || !this.#stream.isTTY) { | ||
return this; | ||
} | ||
for (let i = 0; i < this.linesToClear; i++) { | ||
if (i > 0) { | ||
this.stream.moveCursor(0, -1); | ||
this.#stream.cursorTo(0); | ||
for (let index = 0; index < this.#linesToClear; index++) { | ||
if (index > 0) { | ||
this.#stream.moveCursor(0, -1); | ||
} | ||
this.stream.clearLine(); | ||
this.stream.cursorTo(this.indent); | ||
this.#stream.clearLine(1); | ||
} | ||
this.linesToClear = 0; | ||
if (this.#indent || this.lastIndent !== this.#indent) { | ||
this.#stream.cursorTo(this.#indent); | ||
} | ||
this.lastIndent = this.#indent; | ||
this.#linesToClear = 0; | ||
return this; | ||
@@ -288,3 +273,3 @@ } | ||
render() { | ||
if (this.isSilent) { | ||
if (this.#isSilent) { | ||
return this; | ||
@@ -294,4 +279,4 @@ } | ||
this.clear(); | ||
this.stream.write(this.frame()); | ||
this.linesToClear = this.lineCount; | ||
this.#stream.write(this.frame()); | ||
this.#linesToClear = this.#lineCount; | ||
@@ -306,9 +291,9 @@ return this; | ||
if (this.isSilent) { | ||
if (this.#isSilent) { | ||
return this; | ||
} | ||
if (!this.isEnabled) { | ||
if (!this.#isEnabled) { | ||
if (this.text) { | ||
this.stream.write(`- ${this.text}\n`); | ||
this.#stream.write(`- ${this.text}\n`); | ||
} | ||
@@ -323,8 +308,8 @@ | ||
if (this.hideCursor) { | ||
cliCursor.hide(this.stream); | ||
if (this.#options.hideCursor) { | ||
cliCursor.hide(this.#stream); | ||
} | ||
if (this.discardStdin && process.stdin.isTTY) { | ||
this.isDiscardingStdin = true; | ||
if (this.#options.discardStdin && process.stdin.isTTY) { | ||
this.#isDiscardingStdin = true; | ||
stdinDiscarder.start(); | ||
@@ -334,3 +319,3 @@ } | ||
this.render(); | ||
this.id = setInterval(this.render.bind(this), this.interval); | ||
this.#id = setInterval(this.render.bind(this), this.interval); | ||
@@ -341,17 +326,17 @@ return this; | ||
stop() { | ||
if (!this.isEnabled) { | ||
if (!this.#isEnabled) { | ||
return this; | ||
} | ||
clearInterval(this.id); | ||
this.id = undefined; | ||
this.frameIndex = 0; | ||
clearInterval(this.#id); | ||
this.#id = undefined; | ||
this.#frameIndex = 0; | ||
this.clear(); | ||
if (this.hideCursor) { | ||
cliCursor.show(this.stream); | ||
if (this.#options.hideCursor) { | ||
cliCursor.show(this.#stream); | ||
} | ||
if (this.discardStdin && process.stdin.isTTY && this.isDiscardingStdin) { | ||
if (this.#options.discardStdin && process.stdin.isTTY && this.#isDiscardingStdin) { | ||
stdinDiscarder.stop(); | ||
this.isDiscardingStdin = false; | ||
this.#isDiscardingStdin = false; | ||
} | ||
@@ -379,12 +364,21 @@ | ||
stopAndPersist(options = {}) { | ||
if (this.isSilent) { | ||
if (this.#isSilent) { | ||
return this; | ||
} | ||
const prefixText = options.prefixText || this.prefixText; | ||
const text = options.text || this.text; | ||
const prefixText = options.prefixText ?? this.#prefixText; | ||
const fullPrefixText = this.#getFullPrefixText(prefixText, ' '); | ||
const symbolText = options.symbol ?? ' '; | ||
const text = options.text ?? this.text; | ||
const fullText = (typeof text === 'string') ? ' ' + text : ''; | ||
const suffixText = options.suffixText ?? this.#suffixText; | ||
const fullSuffixText = this.#getFullSuffixText(suffixText, ' '); | ||
const textToWrite = fullPrefixText + symbolText + fullText + fullSuffixText + '\n'; | ||
this.stop(); | ||
this.stream.write(`${this.getFullPrefixText(prefixText, ' ')}${options.symbol || ' '}${fullText}\n`); | ||
this.#stream.write(textToWrite); | ||
@@ -395,27 +389,42 @@ return this; | ||
const oraFactory = function (options) { | ||
export default function ora(options) { | ||
return new Ora(options); | ||
}; | ||
} | ||
module.exports = oraFactory; | ||
export async function oraPromise(action, options) { | ||
const actionIsFunction = typeof action === 'function'; | ||
const actionIsPromise = typeof action.then === 'function'; | ||
module.exports.promise = (action, options) => { | ||
// eslint-disable-next-line promise/prefer-await-to-then | ||
if (typeof action.then !== 'function') { | ||
throw new TypeError('Parameter `action` must be a Promise'); | ||
if (!actionIsFunction && !actionIsPromise) { | ||
throw new TypeError('Parameter `action` must be a Function or a Promise'); | ||
} | ||
const spinner = new Ora(options); | ||
spinner.start(); | ||
const {successText, failText} = typeof options === 'object' | ||
? options | ||
: {successText: undefined, failText: undefined}; | ||
(async () => { | ||
try { | ||
await action; | ||
spinner.succeed(); | ||
} catch { | ||
spinner.fail(); | ||
} | ||
})(); | ||
const spinner = ora(options).start(); | ||
return spinner; | ||
}; | ||
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; | ||
} | ||
} | ||
export {default as spinners} from 'cli-spinners'; |
{ | ||
"name": "ora", | ||
"version": "5.4.1", | ||
"version": "8.0.1", | ||
"description": "Elegant terminal spinner", | ||
@@ -13,4 +13,10 @@ "license": "MIT", | ||
}, | ||
"type": "module", | ||
"exports": { | ||
"types": "./index.d.ts", | ||
"default": "./index.js" | ||
}, | ||
"sideEffects": false, | ||
"engines": { | ||
"node": ">=10" | ||
"node": ">=18" | ||
}, | ||
@@ -41,19 +47,20 @@ "scripts": { | ||
"dependencies": { | ||
"bl": "^4.1.0", | ||
"chalk": "^4.1.0", | ||
"cli-cursor": "^3.1.0", | ||
"cli-spinners": "^2.5.0", | ||
"is-interactive": "^1.0.0", | ||
"is-unicode-supported": "^0.1.0", | ||
"log-symbols": "^4.1.0", | ||
"strip-ansi": "^6.0.0", | ||
"wcwidth": "^1.0.1" | ||
"chalk": "^5.3.0", | ||
"cli-cursor": "^4.0.0", | ||
"cli-spinners": "^2.9.2", | ||
"is-interactive": "^2.0.0", | ||
"is-unicode-supported": "^2.0.0", | ||
"log-symbols": "^6.0.0", | ||
"stdin-discarder": "^0.2.1", | ||
"string-width": "^7.0.0", | ||
"strip-ansi": "^7.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^14.14.35", | ||
"ava": "^2.4.0", | ||
"get-stream": "^6.0.0", | ||
"tsd": "^0.14.0", | ||
"xo": "^0.38.2" | ||
"@types/node": "^20.10.5", | ||
"ava": "^5.3.1", | ||
"get-stream": "^8.0.1", | ||
"transform-tty": "^1.0.11", | ||
"tsd": "^0.30.0", | ||
"xo": "^0.56.0" | ||
} | ||
} |
117
readme.md
@@ -13,5 +13,5 @@ # ora | ||
```sh | ||
npm install ora | ||
``` | ||
$ npm install ora | ||
``` | ||
@@ -21,3 +21,3 @@ ## Usage | ||
```js | ||
const ora = require('ora'); | ||
import ora from 'ora'; | ||
@@ -55,2 +55,8 @@ const spinner = ora('Loading unicorns').start(); | ||
##### suffixText | ||
Type: `string | () => string` | ||
Text or a function that returns text to display after the spinner text. No suffix text will be displayed if set to an empty string. | ||
##### spinner | ||
@@ -61,3 +67,3 @@ | ||
Name of one of the [provided spinners](https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json). See `example.js` in this repo if you want to test out different spinners. On Windows, it will always use the `line` spinner as the Windows command-line doesn't have proper Unicode support. | ||
Name of one of the [provided spinners](#spinners). See `example.js` in this repo if you want to test out different spinners. On Windows, it will always use the `line` spinner as the Windows command-line doesn't have proper Unicode support. | ||
@@ -79,3 +85,3 @@ Or an object like: | ||
Color of the spinner. | ||
The color of the spinner. | ||
@@ -136,6 +142,44 @@ ##### hideCursor | ||
This has no effect on Windows as there's no good way to implement discarding stdin properly there. | ||
This has no effect on Windows as there is no good way to implement discarding stdin properly there. | ||
### Instance | ||
#### .text <sup>get/set</sup> | ||
Change the text after the spinner. | ||
#### .prefixText <sup>get/set</sup> | ||
Change the text before the spinner. | ||
No prefix text will be displayed if set to an empty string. | ||
#### .suffixText <sup>get/set</sup> | ||
Change the text after the spinner text. | ||
No suffix text will be displayed if set to an empty string. | ||
#### .color <sup>get/set</sup> | ||
Change the spinner color. | ||
#### .spinner <sup>get/set</sup> | ||
Change the spinner. | ||
#### .indent <sup>get/set</sup> | ||
Change the spinner indent. | ||
#### .isSpinning <sup>get</sup> | ||
A boolean of whether the instance is currently spinning. | ||
#### .interval <sup>get</sup> | ||
The interval between each frame. | ||
The interval is decided by the chosen spinner. | ||
#### .start(text?) | ||
@@ -165,6 +209,2 @@ | ||
#### .isSpinning | ||
A boolean of whether the instance is currently spinning. | ||
#### .stopAndPersist(options?) | ||
@@ -190,3 +230,3 @@ | ||
Text to be persisted after the symbol | ||
Text to be persisted after the symbol. | ||
@@ -200,2 +240,9 @@ ###### prefixText | ||
###### suffixText | ||
Type: `string`\ | ||
Default: Current `suffixText` | ||
Text to be persisted after the text after the symbol. No suffix text will be displayed if set to an empty string. | ||
<img src="screenshot-2.gif" width="480"> | ||
@@ -215,31 +262,45 @@ | ||
#### .text | ||
### oraPromise(action, text) | ||
### oraPromise(action, options) | ||
Change the text after the spinner. | ||
Starts a spinner for a promise or promise-returning function. The spinner is stopped with `.succeed()` if the promise fulfills or with `.fail()` if it rejects. Returns the promise. | ||
#### .prefixText | ||
```js | ||
import {oraPromise} from 'ora'; | ||
Change the text before the spinner. No prefix text will be displayed if set to an empty string. | ||
await oraPromise(somePromise); | ||
``` | ||
#### .color | ||
#### action | ||
Change the spinner color. | ||
Type: `Promise | ((spinner: ora.Ora) => Promise)` | ||
#### .spinner | ||
#### options | ||
Change the spinner. | ||
Type: `object` | ||
#### .indent | ||
All of the [options](#options) plus the following: | ||
Change the spinner indent. | ||
##### successText | ||
### ora.promise(action, text) | ||
### ora.promise(action, options) | ||
Type: `string | ((result: T) => string) | undefined` | ||
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. | ||
The new text of the spinner when the promise is resolved. | ||
#### action | ||
Keeps the existing text if `undefined`. | ||
Type: `Promise` | ||
##### failText | ||
Type: `string | ((error: Error) => string) | undefined` | ||
The new text of the spinner when the promise is rejected. | ||
Keeps the existing text if `undefined`. | ||
### spinners | ||
Type: `Record<string, Spinner>` | ||
All [provided spinners](https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json). | ||
## FAQ | ||
@@ -252,4 +313,4 @@ | ||
```js | ||
const ora = require('ora'); | ||
const chalk = require('chalk'); | ||
import ora from 'ora'; | ||
import chalk from 'chalk'; | ||
@@ -256,0 +317,0 @@ const spinner = ora(`Loading ${chalk.red('unicorns')}`).start(); |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
26806
15.31%574
8.3%326
23.02%Yes
NaN6
20%2
100%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated
Updated
Updated
Updated