From 25908d87516f07f0464ed99b311411b0451b2934 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Sat, 18 Mar 2023 13:52:53 -0500 Subject: [PATCH 1/5] feat: document public API --- index.d.ts | 21 +++++++++++++++++++++ index.test-d.ts | 4 ++++ readme.md | 21 +++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/index.d.ts b/index.d.ts index ff84da3..9e552dd 100644 --- a/index.d.ts +++ b/index.d.ts @@ -18,6 +18,8 @@ export type Color = export type PrefixTextGenerator = () => string; +export type SuffixTextGenerator = () => string; + export interface Options { /** Text to display after the spinner. @@ -29,6 +31,11 @@ export interface Options { */ readonly prefixText?: string | PrefixTextGenerator; + /** + 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; + /** 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. @@ -130,6 +137,13 @@ export interface PersistOptions { Default: Current `prefixText`. */ readonly 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. + + Default: Current `suffixText`. + */ + readonly suffixText?: string | SuffixTextGenerator; } export interface PromiseOptions extends Options { @@ -161,6 +175,13 @@ export interface Ora { */ prefixText: string; + /** + Change the text or function that returns text after the spinner text. + + No suffix text will be displayed if set to an empty string. + */ + suffixText: string; + /** Change the spinner color. */ diff --git a/index.test-d.ts b/index.test-d.ts index 5c129c0..b97ba8d 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -6,6 +6,8 @@ const spinner = ora('Loading unicorns'); ora({text: 'Loading unicorns'}); ora({prefixText: 'Loading unicorns'}); ora({prefixText: () => 'Loading unicorns dynamically'}); +ora({suffixText: 'Loading unicorns'}); +ora({suffixText: () => 'Loading unicorns dynamically'}); ora({spinner: 'squish'}); ora({spinner: {frames: ['-', '+', '-']}}); ora({spinner: {interval: 80, frames: ['-', '+', '-']}}); @@ -39,6 +41,7 @@ spinner.stopAndPersist(); spinner.stopAndPersist({text: 'all done'}); spinner.stopAndPersist({symbol: '@', text: 'all done'}); spinner.stopAndPersist({prefixText: 'all done'}); +spinner.stopAndPersist({suffixText: 'all done'}); spinner.clear(); spinner.render(); spinner.frame(); @@ -57,6 +60,7 @@ void oraPromise(async () => { }, 'foo'); void oraPromise(async spinner => { spinner.prefixText = 'foo'; + spinner.suffixText = '[loading]'; await resolves; return 7; }, { diff --git a/readme.md b/readme.md index 727556f..e95317f 100644 --- a/readme.md +++ b/readme.md @@ -50,6 +50,12 @@ Type: `string | () => 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. +##### 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 Type: `string | object`\ @@ -142,6 +148,12 @@ Change the text before the spinner. No prefix text will be displayed if set to an empty string. +#### .suffixText get/set + +Change the text after the spinner text. + +No suffix text will be displayed if set to an empty string. + #### .color get/set Change the spinner color. @@ -219,6 +231,15 @@ Text to be persisted before the symbol. No prefix text will be displayed if set +###### 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. + + + #### .clear() Clear the spinner. Returns the instance. From d7c2d6f3762c0841da9558e9092d31935fd8ccd3 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Wed, 22 Mar 2023 15:56:23 -0500 Subject: [PATCH 2/5] fix: add missing period for consistency --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index e95317f..b38f7c9 100644 --- a/readme.md +++ b/readme.md @@ -220,7 +220,7 @@ Symbol to replace the spinner with. Type: `string`\ Default: Current `'text'` -Text to be persisted after the symbol +Text to be persisted after the symbol. ###### prefixText From 6d8e77aee7fe55a0e76cb515d4eb49b96e88213d Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Wed, 22 Mar 2023 16:05:48 -0500 Subject: [PATCH 3/5] feat: add `suffixText` option --- index.js | 45 ++++++++++++++++++++++++++++++++++++++++----- test.js | 48 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index 0d951ad..a061757 100644 --- a/index.js +++ b/index.js @@ -24,6 +24,7 @@ class Ora { #indent; #text; #prefixText; + #suffixText; color; @@ -57,6 +58,7 @@ class Ora { // 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') { @@ -147,6 +149,15 @@ class Ora { this.updateLineCount(); } + get suffixText() { + return this.#suffixText; + } + + set suffixText(value) { + this.#suffixText = value || ''; + this.updateLineCount(); + } + get isSpinning() { return this.#id !== undefined; } @@ -164,12 +175,26 @@ class Ora { return ''; } + 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(' '.repeat(this.#indent) + fullPrefixText + '--' + this.#text).split('\n')) { + for (const line of stripAnsi(fullText).split('\n')) { this.#lineCount += Math.max(1, Math.ceil(wcwidth(line) / columns)); } } @@ -209,8 +234,9 @@ class Ora { 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() { @@ -328,12 +354,21 @@ class Ora { 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); return this; } diff --git a/test.js b/test.js index c2a59a4..bdbddd1 100644 --- a/test.js +++ b/test.js @@ -240,6 +240,16 @@ test('erases wrapped lines', t => { t.is(clearedLines, 3); // Cleared 'foo\n\nbar' t.is(cursorAtRow, -2); + spinner.clear(); + reset(); + spinner.prefixText = 'foo\n'; + spinner.text = '\nbar'; + spinner.suffixText = '\nbaz'; + spinner.render(); + spinner.render(); + t.is(clearedLines, 4); // Cleared 'foo\n\nbar \nbaz' + t.is(cursorAtRow, -3); + spinner.stop(); }); @@ -399,6 +409,30 @@ test('.stopAndPersist() with dynamic prefixText', macro, spinner => { spinner.stopAndPersist({symbol: '&', prefixText: () => 'babeee', text: 'yorkie'}); }, /babeee & yorkie\n$/, {prefixText: () => 'babeee'}); +test('.stopAndPersist() with suffixText', macro, spinner => { + spinner.stopAndPersist({symbol: '@', text: 'foo'}); +}, /@ foo bar\n$/, {suffixText: 'bar'}); + +test('.stopAndPersist() with empty suffixText', macro, spinner => { + spinner.stopAndPersist({symbol: '@', text: 'foo'}); +}, /@ foo\n$/, {suffixText: ''}); + +test('.stopAndPersist() with manual suffixText', macro, spinner => { + spinner.stopAndPersist({symbol: '@', suffixText: 'baz', text: 'foo'}); +}, /@ foo baz\n$/, {suffixText: 'bar'}); + +test('.stopAndPersist() with manual empty suffixText', macro, spinner => { + spinner.stopAndPersist({symbol: '@', suffixText: '', text: 'foo'}); +}, /@ foo\n$/, {suffixText: 'bar'}); + +test('.stopAndPersist() with dynamic suffixText', macro, spinner => { + spinner.stopAndPersist({symbol: '&', suffixText: () => 'babeee', text: 'yorkie'}); +}, /& yorkie babeee\n$/, {suffixText: () => 'babeee'}); + +test('.stopAndPersist() with prefixText and suffixText', macro, spinner => { + spinner.stopAndPersist({symbol: '@', text: 'foo'}); +}, /bar @ foo baz\n$/, {prefixText: 'bar', suffixText: 'baz'}); + // New clear method tests const currentClearMethod = transFormTTY => { @@ -597,21 +631,23 @@ test('new clear method test, erases wrapped lines', t => { currentOra.clear(); currentOra.prefixText = 'foo\n'; currentOra.text = '\nbar'; + currentOra.suffixText = '\nbaz'; currentOra.render(); currentOra.render(); spinner.clear(); spinner.prefixText = 'foo\n'; spinner.text = '\nbar'; + spinner.suffixText = '\nbaz'; spinner.render(); spinner.render(); - t.is(clearedLines(), 3); // Cleared 'foo\n\nbar' - t.is(cursorAtRow(), -2); + t.is(clearedLines(), 4); // Cleared 'foo\n\nbar \nbaz' + t.is(cursorAtRow(), -3); const [sequenceString, clearedSequenceString] = transformTTY.getSequenceStrings(); const [frames, clearedFrames] = transformTTY.getFrames(); - t.is(sequenceString, 'foo\n - \nbar'); + t.is(sequenceString, 'foo\n - \nbar \nbaz'); t.is(sequenceString, clearedSequenceString); t.deepEqual(clearedFrames, [ @@ -631,14 +667,14 @@ test('new clear method test, erases wrapped lines', t => { '- 🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄\n' + '🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄\n' + 'foo', - 'foo\n - \nbar', - 'foo\n - \nbar', + 'foo\n - \nbar \nbaz', + 'foo\n - \nbar \nbaz', ]); t.deepEqual(frames, clearedFrames); const currentClearString = currentClearTTY.toString(); - t.is(currentClearString, 'foo\n - \nbar'); + t.is(currentClearString, 'foo\n - \nbar \nbaz'); const currentFrames = currentClearTTY.getFrames(); t.deepEqual(frames, currentFrames); From 2e55c40bb1a6a8a1eeb251499bd94840a2f00005 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Wed, 22 Mar 2023 16:07:43 -0500 Subject: [PATCH 4/5] fix: move `stopAndPersist#suffixText` --- readme.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/readme.md b/readme.md index b38f7c9..3684b76 100644 --- a/readme.md +++ b/readme.md @@ -229,8 +229,6 @@ Default: Current `prefixText` Text to be persisted before the symbol. No prefix text will be displayed if set to an empty string. - - ###### suffixText Type: `string`\ @@ -238,7 +236,7 @@ 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. - + #### .clear() From 3bba946232a3dcc77685b48c9a11127459764445 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Wed, 22 Mar 2023 16:20:44 -0500 Subject: [PATCH 5/5] feat: update example --- example.js | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/example.js b/example.js index f782a5f..9a0cc80 100644 --- a/example.js +++ b/example.js @@ -1,5 +1,6 @@ import process from 'node:process'; import chalk from 'chalk'; +import logSymbols from 'log-symbols'; import ora from './index.js'; const spinner = ora({ @@ -46,7 +47,30 @@ setTimeout(() => { }, 6000); setTimeout(() => { - spinner.succeed(); -}, 7000); + spinner.prefixText = chalk.dim('[info]'); + spinner.spinner = 'dots'; + spinner.text = 'Loading with prefix text'; +}, 8000); + +setTimeout(() => { + spinner.prefixText = ''; + spinner.suffixText = chalk.dim('[info]'); + spinner.text = 'Loading with suffix text'; +}, 10_000); + +setTimeout(() => { + spinner.prefixText = chalk.dim('[info]'); + spinner.suffixText = chalk.dim('[info]'); + spinner.text = 'Loading with prefix and suffix text'; +}, 12_000); + +setTimeout(() => { + spinner.stopAndPersist({ + prefixText: '', + suffixText: '', + symbol: logSymbols.info, + text: 'Stopping with different text!', + }); +}, 14_000); // $ node example.js nameOfSpinner