Comparing version 6.0.1 to 6.1.0
@@ -50,3 +50,3 @@ import {SpinnerName} from 'cli-spinners'; | ||
/** | ||
Color of the spinner. | ||
The color of the spinner. | ||
@@ -154,7 +154,2 @@ @default 'cyan' | ||
/** | ||
A boolean of whether the instance is currently spinning. | ||
*/ | ||
readonly isSpinning: boolean; | ||
/** | ||
Change the text after the spinner. | ||
@@ -165,5 +160,7 @@ */ | ||
/** | ||
Change the text or function that returns text before the spinner. No prefix text will be displayed if set to an empty string. | ||
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; | ||
prefixText: string; | ||
@@ -176,12 +173,29 @@ /** | ||
/** | ||
Change the spinner. | ||
Change the spinner indent. | ||
*/ | ||
spinner: SpinnerName | Spinner; | ||
indent: number; | ||
/** | ||
Change the spinner indent. | ||
Get the spinner. | ||
*/ | ||
indent: number; | ||
get spinner(): Spinner; | ||
/** | ||
Set the spinner. | ||
*/ | ||
set spinner(spinner: SpinnerName | Spinner); | ||
/** | ||
A boolean of whether the instance is currently spinning. | ||
*/ | ||
get isSpinning(): boolean; | ||
/** | ||
The interval between each frame. | ||
The interval is decided by the chosen spinner. | ||
*/ | ||
get interval(): number; | ||
/** | ||
Start the spinner. | ||
@@ -192,3 +206,3 @@ | ||
*/ | ||
start(text?: string): Ora; | ||
start(text?: string): this; | ||
@@ -200,3 +214,3 @@ /** | ||
*/ | ||
stop(): Ora; | ||
stop(): this; | ||
@@ -209,3 +223,3 @@ /** | ||
*/ | ||
succeed(text?: string): Ora; | ||
succeed(text?: string): this; | ||
@@ -218,3 +232,3 @@ /** | ||
*/ | ||
fail(text?: string): Ora; | ||
fail(text?: string): this; | ||
@@ -227,3 +241,3 @@ /** | ||
*/ | ||
warn(text?: string): Ora; | ||
warn(text?: string): this; | ||
@@ -236,3 +250,3 @@ /** | ||
*/ | ||
info(text?: string): Ora; | ||
info(text?: string): this; | ||
@@ -244,3 +258,3 @@ /** | ||
*/ | ||
stopAndPersist(options?: PersistOptions): Ora; | ||
stopAndPersist(options?: PersistOptions): this; | ||
@@ -252,3 +266,3 @@ /** | ||
*/ | ||
clear(): Ora; | ||
clear(): this; | ||
@@ -260,3 +274,3 @@ /** | ||
*/ | ||
render(): Ora; | ||
render(): this; | ||
@@ -263,0 +277,0 @@ /** |
292
index.js
import process from 'node:process'; | ||
import readline from 'node:readline'; | ||
import chalk from 'chalk'; | ||
@@ -11,90 +10,24 @@ import cliCursor from 'cli-cursor'; | ||
import isUnicodeSupported from 'is-unicode-supported'; | ||
import {BufferListStream} from 'bl'; | ||
import {StdinDiscarder} from './utilities.js'; | ||
const TEXT = Symbol('text'); | ||
const PREFIX_TEXT = Symbol('prefixText'); | ||
const ASCII_ETX_CODE = 0x03; // Ctrl+C emits this code | ||
let stdinDiscarder; | ||
// TODO: Use class fields when ESLint 8 is out. | ||
class Ora { | ||
#linesToClear = 0; | ||
#isDiscardingStdin = false; | ||
#lineCount = 0; | ||
#frameIndex = 0; | ||
#options; | ||
#spinner; | ||
#stream; | ||
#id; | ||
#initialInterval; | ||
#isEnabled; | ||
#isSilent; | ||
#indent; | ||
#text; | ||
#prefixText; | ||
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) { | ||
@@ -111,31 +44,56 @@ if (!stdinDiscarder) { | ||
this.options = { | ||
text: '', | ||
this.#options = { | ||
color: 'cyan', | ||
stream: process.stderr, | ||
discardStdin: true, | ||
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.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; | ||
} | ||
@@ -148,18 +106,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; | ||
@@ -171,23 +128,21 @@ 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.#text = value || ''; | ||
this.updateLineCount(); | ||
@@ -197,7 +152,7 @@ } | ||
get prefixText() { | ||
return this[PREFIX_TEXT]; | ||
return this.#prefixText; | ||
} | ||
set prefixText(value) { | ||
this[PREFIX_TEXT] = value; | ||
this.#prefixText = value || ''; | ||
this.updateLineCount(); | ||
@@ -207,7 +162,8 @@ } | ||
get isSpinning() { | ||
return this.id !== undefined; | ||
return this.#id !== undefined; | ||
} | ||
getFullPrefixText(prefixText = this[PREFIX_TEXT], postfix = ' ') { | ||
if (typeof prefixText === 'string') { | ||
// TODO: Use private methods when targeting Node.js 14. | ||
getFullPrefixText(prefixText = this.#prefixText, postfix = ' ') { | ||
if (typeof prefixText === 'string' && prefixText !== '') { | ||
return prefixText + postfix; | ||
@@ -224,7 +180,8 @@ } | ||
updateLineCount() { | ||
const columns = this.stream.columns || 80; | ||
const fullPrefixText = this.getFullPrefixText(this.prefixText, '-'); | ||
this.lineCount = 0; | ||
for (const line of stripAnsi(' '.repeat(this.indent) + fullPrefixText + '--' + this[TEXT]).split('\n')) { | ||
this.lineCount += Math.max(1, Math.ceil(wcwidth(line) / columns)); | ||
const columns = this.#stream.columns || 80; | ||
const fullPrefixText = this.getFullPrefixText(this.#prefixText, '-'); | ||
this.#lineCount = 0; | ||
for (const line of stripAnsi(' '.repeat(this.#indent) + fullPrefixText + '--' + this.#text).split('\n')) { | ||
this.#lineCount += Math.max(1, Math.ceil(wcwidth(line) / columns)); | ||
} | ||
@@ -234,3 +191,3 @@ } | ||
get isEnabled() { | ||
return this._isEnabled && !this.isSilent; | ||
return this.#isEnabled && !this.#isSilent; | ||
} | ||
@@ -243,7 +200,7 @@ | ||
this._isEnabled = value; | ||
this.#isEnabled = value; | ||
} | ||
get isSilent() { | ||
return this._isSilent; | ||
return this.#isSilent; | ||
} | ||
@@ -256,8 +213,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]; | ||
@@ -268,4 +225,4 @@ 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 : ''; | ||
@@ -277,22 +234,22 @@ | ||
clear() { | ||
if (!this.isEnabled || !this.stream.isTTY) { | ||
if (!this.#isEnabled || !this.#stream.isTTY) { | ||
return this; | ||
} | ||
this.stream.cursorTo(0); | ||
this.#stream.cursorTo(0); | ||
for (let index = 0; index < this.linesToClear; index++) { | ||
for (let index = 0; index < this.#linesToClear; index++) { | ||
if (index > 0) { | ||
this.stream.moveCursor(0, -1); | ||
this.#stream.moveCursor(0, -1); | ||
} | ||
this.stream.clearLine(1); | ||
this.#stream.clearLine(1); | ||
} | ||
if (this.indent || this.lastIndent !== this.indent) { | ||
this.stream.cursorTo(this.indent); | ||
if (this.#indent || this.lastIndent !== this.#indent) { | ||
this.#stream.cursorTo(this.#indent); | ||
} | ||
this.lastIndent = this.indent; | ||
this.linesToClear = 0; | ||
this.lastIndent = this.#indent; | ||
this.#linesToClear = 0; | ||
@@ -303,3 +260,3 @@ return this; | ||
render() { | ||
if (this.isSilent) { | ||
if (this.#isSilent) { | ||
return this; | ||
@@ -309,4 +266,4 @@ } | ||
this.clear(); | ||
this.stream.write(this.frame()); | ||
this.linesToClear = this.lineCount; | ||
this.#stream.write(this.frame()); | ||
this.#linesToClear = this.#lineCount; | ||
@@ -321,9 +278,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`); | ||
} | ||
@@ -338,8 +295,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(); | ||
@@ -349,3 +306,3 @@ } | ||
this.render(); | ||
this.id = setInterval(this.render.bind(this), this.interval); | ||
this.#id = setInterval(this.render.bind(this), this.interval); | ||
@@ -356,17 +313,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; | ||
} | ||
@@ -394,7 +351,7 @@ | ||
stopAndPersist(options = {}) { | ||
if (this.isSilent) { | ||
if (this.#isSilent) { | ||
return this; | ||
} | ||
const prefixText = options.prefixText || this.prefixText; | ||
const prefixText = options.prefixText || this.#prefixText; | ||
const text = options.text || this.text; | ||
@@ -404,3 +361,3 @@ const fullText = (typeof text === 'string') ? ' ' + text : ''; | ||
this.stop(); | ||
this.stream.write(`${this.getFullPrefixText(prefixText, ' ')}${options.symbol || ' '}${fullText}\n`); | ||
this.#stream.write(`${this.getFullPrefixText(prefixText, ' ')}${options.symbol || ' '}${fullText}\n`); | ||
@@ -417,3 +374,2 @@ return this; | ||
const actionIsFunction = typeof action === 'function'; | ||
// eslint-disable-next-line promise/prefer-await-to-then | ||
const actionIsPromise = typeof action.then === 'function'; | ||
@@ -420,0 +376,0 @@ |
{ | ||
"name": "ora", | ||
"version": "6.0.1", | ||
"version": "6.1.0", | ||
"description": "Elegant terminal spinner", | ||
@@ -23,3 +23,4 @@ "license": "MIT", | ||
"index.js", | ||
"index.d.ts" | ||
"index.d.ts", | ||
"utilities.js" | ||
], | ||
@@ -44,8 +45,8 @@ "keywords": [ | ||
"bl": "^5.0.0", | ||
"chalk": "^4.1.2", | ||
"chalk": "^5.0.0", | ||
"cli-cursor": "^4.0.0", | ||
"cli-spinners": "^2.6.0", | ||
"cli-spinners": "^2.6.1", | ||
"is-interactive": "^2.0.0", | ||
"is-unicode-supported": "^1.1.0", | ||
"log-symbols": "^5.0.0", | ||
"log-symbols": "^5.1.0", | ||
"strip-ansi": "^7.0.1", | ||
@@ -55,9 +56,9 @@ "wcwidth": "^1.0.1" | ||
"devDependencies": { | ||
"@types/node": "^16.9.1", | ||
"ava": "^3.15.0", | ||
"@types/node": "^17.0.18", | ||
"ava": "^4.0.1", | ||
"get-stream": "^6.0.1", | ||
"transform-tty": "^1.0.11", | ||
"tsd": "^0.17.0", | ||
"xo": "^0.44.0" | ||
"tsd": "^0.19.1", | ||
"xo": "^0.48.0" | ||
} | ||
} |
@@ -13,5 +13,5 @@ # ora | ||
```sh | ||
npm install ora | ||
``` | ||
$ npm install ora | ||
``` | ||
@@ -76,3 +76,3 @@ ## Usage | ||
Color of the spinner. | ||
The color of the spinner. | ||
@@ -137,2 +137,34 @@ ##### hideCursor | ||
#### .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. | ||
#### .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?) | ||
@@ -162,6 +194,2 @@ | ||
#### .isSpinning | ||
A boolean of whether the instance is currently spinning. | ||
#### .stopAndPersist(options?) | ||
@@ -210,22 +238,2 @@ | ||
#### .text | ||
Change the text after the spinner. | ||
#### .prefixText | ||
Change the text before the spinner. No prefix text will be displayed if set to an empty string. | ||
#### .color | ||
Change the spinner color. | ||
#### .spinner | ||
Change the spinner. | ||
#### .indent | ||
Change the spinner indent. | ||
### oraPromise(action, text) | ||
@@ -232,0 +240,0 @@ ### oraPromise(action, options) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
26232
6
604
301
2
- Removedansi-styles@4.3.0(transitive)
- Removedchalk@4.1.2(transitive)
- Removedcolor-convert@2.0.1(transitive)
- Removedcolor-name@1.1.4(transitive)
- Removedhas-flag@4.0.0(transitive)
- Removedsupports-color@7.2.0(transitive)
Updatedchalk@^5.0.0
Updatedcli-spinners@^2.6.1
Updatedlog-symbols@^5.1.0