From d64ab0bc132a65316195488b9ad1554a06ec3f88 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Thu, 2 Feb 2023 15:40:59 -0600 Subject: [PATCH] feat: expose stdout hooks --- .changeset/twenty-rabbits-work.md | 5 +++++ src/messages/index.ts | 25 +++++++++++++------------ src/prompt/prompt.js | 6 +++--- src/spinner/index.ts | 24 ++++++++++++------------ 4 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 .changeset/twenty-rabbits-work.md diff --git a/.changeset/twenty-rabbits-work.md b/.changeset/twenty-rabbits-work.md new file mode 100644 index 0000000..c500941 --- /dev/null +++ b/.changeset/twenty-rabbits-work.md @@ -0,0 +1,5 @@ +--- +"@astrojs/cli-kit": minor +--- + +Expose `stdout` hooks to all functions diff --git a/src/messages/index.ts b/src/messages/index.ts index d2598e0..57ec3d9 100644 --- a/src/messages/index.ts +++ b/src/messages/index.ts @@ -1,18 +1,19 @@ import readline from 'node:readline'; import color from 'chalk'; -import logUpdate from 'log-update'; +import { createLogUpdate } from 'log-update'; import { random, randomBetween, sleep, useAscii } from '../utils/index.js' import { action } from '../prompt/util/action.js'; import { strip } from '../prompt/util/clear.js'; -export const say = async (messages: string | string[] = [], { clear = false, hat = '' } = {}) => { - const rl = readline.createInterface({ input: process.stdin, escapeCodeTimeout: 50 }); - readline.emitKeypressEvents(process.stdin, rl); +export const say = async (messages: string | string[] = [], { clear = false, hat = '', stdin = process.stdin, stdout = process.stdout } = {}) => { + const rl = readline.createInterface({ input: stdin, escapeCodeTimeout: 50 }); + const logUpdate = createLogUpdate(stdout, { showCursor: false }); + readline.emitKeypressEvents(stdin, rl); let i = 0; let cancelled = false; const done = async () => { - process.stdin.off('keypress', done) - if (process.stdin.isTTY) process.stdin.setRawMode(false); + stdin.off('keypress', done) + if (stdin.isTTY) stdin.setRawMode(false); rl.close(); cancelled = true; if (i < messages.length - 1) { @@ -24,9 +25,9 @@ export const say = async (messages: string | string[] = [], { clear = false, hat } } - if (process.stdin.isTTY) process.stdin.setRawMode(true); - process.stdin.on('keypress', (str, key) => { - if (process.stdin.isTTY) process.stdin.setRawMode(true); + if (stdin.isTTY) stdin.setRawMode(true); + stdin.on('keypress', (str, key) => { + if (stdin.isTTY) stdin.setRawMode(true); const k = action(key, true); if (k === 'abort') { done(); @@ -73,11 +74,11 @@ export const say = async (messages: string | string[] = [], { clear = false, hat if (!cancelled) await sleep(randomBetween(1200, 1400)); i++; } - process.stdin.off('keypress', done); + stdin.off('keypress', done); await sleep(100); done(); - if (process.stdin.isTTY) process.stdin.setRawMode(false); - process.stdin.removeAllListeners('keypress') + if (stdin.isTTY) stdin.setRawMode(false); + stdin.removeAllListeners('keypress') } export const label = (text: string, c = color.bgHex('#883AE2'), t = color.whiteBright) => c(` ${t(text)} `) diff --git a/src/prompt/prompt.js b/src/prompt/prompt.js index 1414cc8..f72dc30 100644 --- a/src/prompt/prompt.js +++ b/src/prompt/prompt.js @@ -3,7 +3,7 @@ const noop = (v) => v; function toPrompt(type, args, opts = {}) { return new Promise((res, rej) => { - const p = new el[type](args); + const p = new el[type](args, opts); const onAbort = opts.onAbort || noop; const onSubmit = opts.onSubmit || noop; const onExit = opts.onExit || noop; @@ -24,7 +24,7 @@ const prompts = { /** @type {import('../../types').default} */ export default async function prompt( questions = [], - { onSubmit = noop, onCancel = () => process.exit(0) } = {} + { onSubmit = noop, onCancel = () => process.exit(0), stdin = process.stdin, stdout = process.stdout } = {} ) { const answers = {}; questions = [].concat(questions); @@ -35,7 +35,7 @@ export default async function prompt( try { // Get the injected answer if there is one or prompt the user - answer = await prompts[type](question); + answer = await prompts[type](Object.assign({ stdin, stdout }, question)); answers[name] = answer; quit = await onSubmit(question, answer, answers); } catch (err) { diff --git a/src/spinner/index.ts b/src/spinner/index.ts index 8f68cf9..6ef99eb 100644 --- a/src/spinner/index.ts +++ b/src/spinner/index.ts @@ -48,25 +48,25 @@ function getGradientAnimFrames() { * @param text display text next to rocket * @returns Ora spinner for running .stop() */ -async function gradient(text: string) { +async function gradient(text: string, { stdin = process.stdin, stdout = process.stdout } = {}) { let i = 0; const frames = getGradientAnimFrames(); let interval: NodeJS.Timeout; - const rl = readline.createInterface({ input: process.stdin, escapeCodeTimeout: 50 }); - readline.emitKeypressEvents(process.stdin, rl); + const rl = readline.createInterface({ input: stdin, escapeCodeTimeout: 50 }); + readline.emitKeypressEvents(stdin, rl); - if (process.stdin.isTTY) process.stdin.setRawMode(true); + if (stdin.isTTY) stdin.setRawMode(true); const keypress = () => { - if (process.stdin.isTTY) process.stdin.setRawMode(true); - process.stdout.write(cursor.hide + erase.lines(2)); + if (stdin.isTTY) stdin.setRawMode(true); + stdout.write(cursor.hide + erase.lines(2)); }; let done = false; const spinner = { start() { - process.stdout.write(cursor.hide); - process.stdin.on('keypress', keypress); + stdout.write(cursor.hide); + stdin.on('keypress', keypress); logUpdate(`${frames[0]} ${text}`); const loop = async () => { @@ -86,7 +86,7 @@ async function gradient(text: string) { }, stop() { done = true; - process.stdin.removeListener('keypress', keypress); + stdin.removeListener('keypress', keypress); clearInterval(interval); logUpdate.clear(); } @@ -95,14 +95,14 @@ async function gradient(text: string) { return spinner; } -export async function spinner({ start, end, while: update = () => sleep(100) }: { start: string, end: string, while: (...args: any) => Promise }) { +export async function spinner({ start, end, while: update = () => sleep(100) }: { start: string, end: string, while: (...args: any) => Promise }, { stdin = process.stdin, stdout = process.stdout } = {}) { const act = update(); const tooslow = Object.create(null); const result = await Promise.race([sleep(500).then(() => tooslow), act]); if (result === tooslow) { - const loading = await gradient(chalk.green(start)); + const loading = await gradient(chalk.green(start), { stdin, stdout }); await act; loading.stop(); }; - console.log(`${' '.repeat(5)} ${chalk.green('✔')} ${chalk.green(end)}`) + stdout.write(`\n${' '.repeat(5)} ${chalk.green('✔')} ${chalk.green(end)}`) }