diff --git a/index.d.ts b/index.d.ts index 392deb2..a25a607 100644 --- a/index.d.ts +++ b/index.d.ts @@ -92,6 +92,13 @@ declare namespace ora { */ readonly isEnabled?: boolean; + /** + Disable the spinner and all log text. All output is suppressed and `isEnabled` will be considered `false`. + + @default false + */ + readonly isSilent?: 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. diff --git a/index.js b/index.js index c18218a..c91c7e4 100644 --- a/index.js +++ b/index.js @@ -122,6 +122,7 @@ class Ora { 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; // Set *after* `this.stream` this.text = this.options.text; @@ -207,6 +208,30 @@ class Ora { this.updateLineCount(); } + get isEnabled() { + return this._isEnabled && !this.isSilent; + } + + set isEnabled(value) { + if (typeof value !== 'boolean') { + throw new TypeError('The `isEnabled` option must be a boolean'); + } + + this._isEnabled = value; + } + + get isSilent() { + return this._isSilent; + } + + set isSilent(value) { + if (typeof value !== 'boolean') { + throw new TypeError('The `isSilent` option must be a boolean'); + } + + this._isSilent = value; + } + frame() { const {frames} = this.spinner; let frame = frames[this.frameIndex]; @@ -242,6 +267,10 @@ class Ora { } render() { + if (this.isSilent) { + return this; + } + this.clear(); this.stream.write(this.frame()); this.linesToClear = this.lineCount; @@ -254,6 +283,10 @@ class Ora { this.text = text; } + if (this.isSilent) { + return this; + } + if (!this.isEnabled) { if (this.text) { this.stream.write(`- ${this.text}\n`); @@ -319,6 +352,10 @@ class Ora { } stopAndPersist(options = {}) { + if (this.isSilent) { + return this; + } + const prefixText = options.prefixText || this.prefixText; const fullPrefixText = (typeof prefixText === 'string' && prefixText !== '') ? prefixText + ' ' : ''; const text = options.text || this.text; diff --git a/index.test-d.ts b/index.test-d.ts index 3ff2f99..65c2703 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -15,6 +15,7 @@ ora({indent: 1}); ora({interval: 80}); ora({stream: new PassThroughStream()}); ora({isEnabled: true}); +ora({isSilent: false}); ora({discardStdin: true}); spinner.color = 'yellow'; @@ -48,5 +49,6 @@ promise(resolves, { stream: new PassThroughStream(), text: 'foo', color: 'blue', - isEnabled: true + isEnabled: true, + isSilent: false }); diff --git a/readme.md b/readme.md index d2f0447..d173018 100644 --- a/readme.md +++ b/readme.md @@ -114,6 +114,13 @@ Force enable/disable the spinner. If not specified, the spinner will be enabled 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. +##### isSilent + +Type: `boolean`\ +Default: `false` + +Disable the spinner and all log text. All output is suppressed and `isEnabled` will be considered `false`. + ##### discardStdin Type: `boolean`\ diff --git a/test.js b/test.js index bdb61d9..b0d5c07 100644 --- a/test.js +++ b/test.js @@ -24,6 +24,7 @@ const doSpinner = async (fn, extraOptions = {}) => { text: 'foo', color: false, isEnabled: true, + isSilent: false, ...extraOptions }); @@ -130,6 +131,19 @@ test('.stopAndPersist() - isEnabled:false outputs text', macro, spinner => { spinner.stopAndPersist({symbol: '@', text: 'all done'}); }, /- foo\n@ all done\n$/, {isEnabled: false}); +test('.start() - isSilent:true no output', macro, spinner => { + spinner.stop(); +}, /^(?![\s\S])/, {isSilent: true}); + +test('.stopAndPersist() - isSilent:true no output', macro, spinner => { + spinner.stopAndPersist({symbol: '@', text: 'all done'}); +}, /^(?![\s\S])/, {isSilent: true}); + +test('.stopAndPersist() - isSilent:true can be disabled', macro, spinner => { + spinner.isSilent = false; + spinner.stopAndPersist({symbol: '@', text: 'all done'}); +}, /@ all done\n$/, {isSilent: true}); + test('.promise() - resolves', async t => { const stream = getPassThroughStream(); const output = getStream(stream); @@ -294,16 +308,19 @@ test('set the correct interval when changing spinner (string case)', t => { spinner.spinner = 'layer'; - t.is(spinner.interval, 150); + const expectedInterval = process.platform === 'win32' ? 130 : 150; + t.is(spinner.interval, expectedInterval); }); -test('throw when incorrect spinner', t => { - const ora = new Ora(); +if (process.platform !== 'win32') { + test('throw when incorrect spinner', t => { + const ora = new Ora(); - t.throws(() => { - ora.spinner = 'random-string-12345'; - }, /no built-in spinner/); -}); + t.throws(() => { + ora.spinner = 'random-string-12345'; + }, /no built-in spinner/); + }); +} test('indent option', t => { const stream = getPassThroughStream();