diff --git a/src/cli-ux/action/base.ts b/src/cli-ux/action/base.ts index cfb51b50b..a93f7161b 100644 --- a/src/cli-ux/action/base.ts +++ b/src/cli-ux/action/base.ts @@ -1,201 +1,203 @@ import castArray from 'lodash/castArray' import {inspect} from 'util' -export interface ITask { - action: string; - status: string | undefined; - active: boolean; +export namespace CliUx { + export interface ITask { + action: string; + status: string | undefined; + active: boolean; + } + export type ActionType = 'spinner' | 'simple' | 'debug' + export interface Options { + stdout?: boolean; + } } -export type ActionType = 'spinner' | 'simple' | 'debug' +export namespace CliUx { + export class ActionBase { + type!: ActionType -export interface Options { - stdout?: boolean; -} + std: 'stdout' | 'stderr' = 'stderr' -export class ActionBase { - type!: ActionType + protected stdmocks?: ['stdout' | 'stderr', string[]][] - std: 'stdout' | 'stderr' = 'stderr' + private stdmockOrigs = { + stdout: process.stdout.write, + stderr: process.stderr.write, + } - protected stdmocks?: ['stdout' | 'stderr', string[]][] + public start(action: string, status?: string, opts: Options = {}) { + this.std = opts.stdout ? 'stdout' : 'stderr' + const task = {action, status, active: Boolean(this.task && this.task.active)} + this.task = task - private stdmockOrigs = { - stdout: process.stdout.write, - stderr: process.stderr.write, - } + this._start() + task.active = true + this._stdout(true) + } - public start(action: string, status?: string, opts: Options = {}) { - this.std = opts.stdout ? 'stdout' : 'stderr' - const task = {action, status, active: Boolean(this.task && this.task.active)} - this.task = task + public stop(msg = 'done') { + const task = this.task + if (!task) { + return + } - this._start() - task.active = true - this._stdout(true) - } + this._stop(msg) + task.active = false + this.task = undefined + this._stdout(false) + } - public stop(msg = 'done') { - const task = this.task - if (!task) { - return + private get globals(): { action: { task?: ITask }; output: string | undefined } { + (global as any)['cli-ux'] = (global as any)['cli-ux'] || {} + const globals = (global as any)['cli-ux'] + globals.action = globals.action || {} + return globals } - this._stop(msg) - task.active = false - this.task = undefined - this._stdout(false) - } + public get task(): ITask | undefined { + return this.globals.action.task + } - private get globals(): { action: { task?: ITask }; output: string | undefined } { - (global as any)['cli-ux'] = (global as any)['cli-ux'] || {} - const globals = (global as any)['cli-ux'] - globals.action = globals.action || {} - return globals - } + public set task(task: ITask | undefined) { + this.globals.action.task = task + } - public get task(): ITask | undefined { - return this.globals.action.task - } + protected get output(): string | undefined { + return this.globals.output + } - public set task(task: ITask | undefined) { - this.globals.action.task = task - } + protected set output(output: string | undefined) { + this.globals.output = output + } - protected get output(): string | undefined { - return this.globals.output - } + get running(): boolean { + return Boolean(this.task) + } - protected set output(output: string | undefined) { - this.globals.output = output - } + get status(): string | undefined { + return this.task ? this.task.status : undefined + } - get running(): boolean { - return Boolean(this.task) - } + set status(status: string | undefined) { + const task = this.task + if (!task) { + return + } - get status(): string | undefined { - return this.task ? this.task.status : undefined - } + if (task.status === status) { + return + } - set status(status: string | undefined) { - const task = this.task - if (!task) { - return + this._updateStatus(status, task.status) + task.status = status } - if (task.status === status) { - return - } + public async pauseAsync(fn: () => Promise, icon?: string) { + const task = this.task + const active = task && task.active + if (task && active) { + this._pause(icon) + this._stdout(false) + task.active = false + } - this._updateStatus(status, task.status) - task.status = status - } + const ret = await fn() + if (task && active) { + this._resume() + } - public async pauseAsync(fn: () => Promise, icon?: string) { - const task = this.task - const active = task && task.active - if (task && active) { - this._pause(icon) - this._stdout(false) - task.active = false + return ret } - const ret = await fn() - if (task && active) { - this._resume() - } + public pause(fn: () => any, icon?: string) { + const task = this.task + const active = task && task.active + if (task && active) { + this._pause(icon) + this._stdout(false) + task.active = false + } - return ret - } + const ret = fn() + if (task && active) { + this._resume() + } - public pause(fn: () => any, icon?: string) { - const task = this.task - const active = task && task.active - if (task && active) { - this._pause(icon) - this._stdout(false) - task.active = false + return ret } - const ret = fn() - if (task && active) { - this._resume() + protected _start() { + throw new Error('not implemented') } - return ret - } - - protected _start() { - throw new Error('not implemented') - } - - protected _stop(_: string) { - throw new Error('not implemented') - } + protected _stop(_: string) { + throw new Error('not implemented') + } - protected _resume() { - if (this.task) this.start(this.task.action, this.task.status) - } + protected _resume() { + if (this.task) this.start(this.task.action, this.task.status) + } - protected _pause(_?: string) { - throw new Error('not implemented') - } + protected _pause(_?: string) { + throw new Error('not implemented') + } - protected _updateStatus(_: string | undefined, __?: string) {} - - // mock out stdout/stderr so it doesn't screw up the rendering - protected _stdout(toggle: boolean) { - try { - const outputs: ['stdout', 'stderr'] = ['stdout', 'stderr'] - if (toggle) { - if (this.stdmocks) return - this.stdmockOrigs = { - stdout: process.stdout.write, - stderr: process.stderr.write, - } + protected _updateStatus(_: string | undefined, __?: string) {} + + // mock out stdout/stderr so it doesn't screw up the rendering + protected _stdout(toggle: boolean) { + try { + const outputs: ['stdout', 'stderr'] = ['stdout', 'stderr'] + if (toggle) { + if (this.stdmocks) return + this.stdmockOrigs = { + stdout: process.stdout.write, + stderr: process.stderr.write, + } - this.stdmocks = [] - for (const std of outputs) { - (process[std] as any).write = (...args: any[]) => { - this.stdmocks!.push([std, args] as ['stdout' | 'stderr', string[]]) + this.stdmocks = [] + for (const std of outputs) { + (process[std] as any).write = (...args: any[]) => { + this.stdmocks!.push([std, args] as ['stdout' | 'stderr', string[]]) + } } + } else { + if (!this.stdmocks) return + // this._write('stderr', '\nresetstdmock\n\n\n') + delete this.stdmocks + for (const std of outputs) process[std].write = this.stdmockOrigs[std] as any } - } else { - if (!this.stdmocks) return - // this._write('stderr', '\nresetstdmock\n\n\n') - delete this.stdmocks - for (const std of outputs) process[std].write = this.stdmockOrigs[std] as any + } catch (error) { + this._write('stderr', inspect(error)) } - } catch (error) { - this._write('stderr', inspect(error)) } - } - // flush mocked stdout/stderr - protected _flushStdout() { - try { - let output = '' - let std: 'stdout' | 'stderr' | undefined - while (this.stdmocks && this.stdmocks.length > 0) { - const cur = this.stdmocks.shift() as ['stdout' | 'stderr', string[]] - std = cur[0] - this._write(std, cur[1]) - output += (cur[1][0] as any).toString('utf8') - } - // add newline if there isn't one already - // otherwise we'll just overwrite it when we render + // flush mocked stdout/stderr + protected _flushStdout() { + try { + let output = '' + let std: 'stdout' | 'stderr' | undefined + while (this.stdmocks && this.stdmocks.length > 0) { + const cur = this.stdmocks.shift() as ['stdout' | 'stderr', string[]] + std = cur[0] + this._write(std, cur[1]) + output += (cur[1][0] as any).toString('utf8') + } + // add newline if there isn't one already + // otherwise we'll just overwrite it when we render - if (output && std && output[output.length - 1] !== '\n') { - this._write(std, '\n') + if (output && std && output[output.length - 1] !== '\n') { + this._write(std, '\n') + } + } catch (error) { + this._write('stderr', inspect(error)) } - } catch (error) { - this._write('stderr', inspect(error)) } - } - // write to the real stdout/stderr - protected _write(std: 'stdout' | 'stderr', s: string | string[]) { - this.stdmockOrigs[std].apply(process[std], castArray(s) as [string]) + // write to the real stdout/stderr + protected _write(std: 'stdout' | 'stderr', s: string | string[]) { + this.stdmockOrigs[std].apply(process[std], castArray(s) as [string]) + } } } diff --git a/src/cli-ux/action/simple.ts b/src/cli-ux/action/simple.ts index 48722ae32..223eaeeb8 100644 --- a/src/cli-ux/action/simple.ts +++ b/src/cli-ux/action/simple.ts @@ -1,7 +1,7 @@ -import {ActionBase, ActionType} from './base' +import {CliUx} from './base' -export default class SimpleAction extends ActionBase { - public type: ActionType = 'simple' +export default class SimpleAction extends CliUx.ActionBase { + public type: CliUx.ActionType = 'simple' protected _start() { const task = this.task diff --git a/src/cli-ux/action/spinner.ts b/src/cli-ux/action/spinner.ts index 936d602c2..ba08ce475 100644 --- a/src/cli-ux/action/spinner.ts +++ b/src/cli-ux/action/spinner.ts @@ -5,18 +5,17 @@ import * as supportsColor from 'supports-color' import deps from '../deps' -import {ActionBase, ActionType} from './base' +import {CliUx} from './base' /* eslint-disable-next-line node/no-missing-require */ const spinners = require('./spinners') - function color(s: string): string { if (!supportsColor) return s const has256 = supportsColor.stdout ? supportsColor.stdout.has256 : (process.env.TERM || '').includes('256') return has256 ? `\u001B[38;5;104m${s}${deps.ansiStyles.reset.open}` : chalk.magenta(s) } -export default class SpinnerAction extends ActionBase { - public type: ActionType = 'spinner' +export default class SpinnerAction extends CliUx.ActionBase { + public type: CliUx.ActionType = 'spinner' spinner?: NodeJS.Timeout diff --git a/src/cli-ux/config.ts b/src/cli-ux/config.ts index e0a110e1c..a17ae1554 100644 --- a/src/cli-ux/config.ts +++ b/src/cli-ux/config.ts @@ -1,17 +1,18 @@ import * as semver from 'semver' -import {ActionBase} from './action/base' +import {CliUx as AB} from './action/base' const version = semver.parse(require('../../package.json').version)! export type Levels = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' -export interface ConfigMessage { - type: 'config'; - prop: string; - value: any; +export namespace CliUx { + export interface ConfigMessage { + type: 'config'; + prop: string; + value: any; + } } - const g: any = global const globals = g['cli-ux'] || (g['cli-ux'] = {}) @@ -27,39 +28,41 @@ const Action = actionType === 'spinner' ? require('./action/spinner').default : const PrideAction = actionType === 'spinner' ? require('./action/pride-spinner').default : require('./action/simple').default /* eslint-enable node/no-missing-require */ -export class UxConfig { - outputLevel: Levels = 'info' +export namespace CliUx { + export class Config { + outputLevel: Levels = 'info' - action: ActionBase = new Action() + action: AB.ActionBase = new Action() - prideAction: ActionBase = new PrideAction() + prideAction: AB.ActionBase = new PrideAction() - errorsHandled = false + errorsHandled = false - showStackTrace = true + showStackTrace = true - get debug(): boolean { - return globals.debug || process.env.DEBUG === '*' - } + get debug(): boolean { + return globals.debug || process.env.DEBUG === '*' + } - set debug(v: boolean) { - globals.debug = v - } + set debug(v: boolean) { + globals.debug = v + } - get context(): any { - return globals.context || {} - } + get context(): any { + return globals.context || {} + } - set context(v: any) { - globals.context = v + set context(v: any) { + globals.context = v + } } } - function fetch() { if (globals[version.major]) return globals[version.major] - globals[version.major] = new UxConfig() + globals[version.major] = new CliUx.Config() return globals[version.major] } -export const config: UxConfig = fetch() -export default config +export namespace CliUx { + export const config: Config = fetch() +} diff --git a/src/cli-ux/deps.ts b/src/cli-ux/deps.ts index 329486e84..02d0ff863 100644 --- a/src/cli-ux/deps.ts +++ b/src/cli-ux/deps.ts @@ -1,4 +1,5 @@ /* eslint-disable node/no-missing-require */ + export default { get stripAnsi(): (string: string) => string { return require('strip-ansi') diff --git a/src/cli-ux/exit.ts b/src/cli-ux/exit.ts index 4f036df30..6174d0d4a 100644 --- a/src/cli-ux/exit.ts +++ b/src/cli-ux/exit.ts @@ -1,17 +1,19 @@ -export class ExitError extends Error { - public 'cli-ux': { - exit: number; - } +export namespace CliUx { + export class ExitError extends Error { + public 'cli-ux': { + exit: number; + } - public code: 'EEXIT' + public code: 'EEXIT' - public error?: Error + public error?: Error - constructor(status: number, error?: Error) { - const code = 'EEXIT' - super(error ? error.message : `${code}: ${status}`) - this.error = error - this['cli-ux'] = {exit: status} - this.code = code + constructor(status: number, error?: Error) { + const code = 'EEXIT' + super(error ? error.message : `${code}: ${status}`) + this.error = error + this['cli-ux'] = {exit: status} + this.code = code + } } } diff --git a/src/cli-ux/index.ts b/src/cli-ux/index.ts index 68f56e395..d2d855062 100644 --- a/src/cli-ux/index.ts +++ b/src/cli-ux/index.ts @@ -1,12 +1,10 @@ import * as Errors from '../errors' import * as util from 'util' -import {ActionBase} from './action/base' -import {config, UxConfig} from './config' +import * as uxConfig from './config' import deps from './deps' -import {ExitError} from './exit' -import {IPromptOptions} from './prompt' import * as Table from './styled/table' +import {CliUx as BaseCliUx} from './action/base' const hyperlinker = require('hyperlinker') @@ -18,7 +16,7 @@ function timeout(p: Promise, ms: number) { }) } - return Promise.race([p, wait(ms, true).then(() => ux.error('timed out'))]) + return Promise.race([p, wait(ms, true).then(() => CliUx.ux.error('timed out'))]) } async function flush() { @@ -29,118 +27,117 @@ async function flush() { return p } -export const ux = { - config, - - warn: Errors.warn, - error: Errors.error, - exit: Errors.exit, - - get prompt() { - return deps.prompt.prompt - }, - /** +export namespace CliUx { + export const ux = { + config: uxConfig.CliUx.config, + warn: Errors.warn, + error: Errors.error, + exit: Errors.exit, + + get prompt() { + return deps.prompt.CliUx.prompt + }, + /** * "press anykey to continue" */ - get anykey() { - return deps.prompt.anykey - }, - get confirm() { - return deps.prompt.confirm - }, - get action() { - return config.action - }, - get prideAction() { - return config.prideAction - }, - styledObject(obj: any, keys?: string[]) { - ux.info(deps.styledObject(obj, keys)) - }, - get styledHeader() { - return deps.styledHeader - }, - get styledJSON() { - return deps.styledJSON - }, - get table() { - return deps.table - }, - get tree() { - return deps.tree - }, - get open() { - return deps.open - }, - get wait() { - return deps.wait - }, - get progress() { - return deps.progress - }, - - async done() { - config.action.stop() - // await flushStdout() - }, - - trace(format: string, ...args: string[]) { - if (this.config.outputLevel === 'trace') { - process.stdout.write(util.format(format, ...args) + '\n') - } - }, - - debug(format: string, ...args: string[]) { - if (['trace', 'debug'].includes(this.config.outputLevel)) { + get anykey() { + return deps.prompt.CliUx.anykey + }, + get confirm() { + return deps.prompt.CliUx.confirm + }, + get action() { + return uxConfig.CliUx.config.action + }, + get prideAction() { + return uxConfig.CliUx.config.prideAction + }, + styledObject(obj: any, keys?: string[]) { + ux.info(deps.styledObject(obj, keys)) + }, + get styledHeader() { + return deps.styledHeader + }, + get styledJSON() { + return deps.styledJSON + }, + get table() { + return deps.table + }, + get tree() { + return deps.tree + }, + get open() { + return deps.open + }, + get wait() { + return deps.wait + }, + get progress() { + return deps.progress + }, + + async done() { + uxConfig.CliUx.config.action.stop() + // await flushStdout() + }, + + trace(format: string, ...args: string[]) { + if (this.config.outputLevel === 'trace') { + process.stdout.write(util.format(format, ...args) + '\n') + } + }, + + debug(format: string, ...args: string[]) { + if (['trace', 'debug'].includes(this.config.outputLevel)) { + process.stdout.write(util.format(format, ...args) + '\n') + } + }, + + info(format: string, ...args: string[]) { process.stdout.write(util.format(format, ...args) + '\n') - } - }, - - info(format: string, ...args: string[]) { - process.stdout.write(util.format(format, ...args) + '\n') - }, - - log(format?: string, ...args: string[]) { - this.info(format || '', ...args) - }, - - url(text: string, uri: string, params = {}) { - const supports = require('supports-hyperlinks') - if (supports.stdout) { - this.log(hyperlinker(text, uri, params)) - } else { - this.log(uri) - } - }, - - annotation(text: string, annotation: string) { - const supports = require('supports-hyperlinks') - if (supports.stdout) { - // \u001b]8;;https://google.com\u0007sometext\u001b]8;;\u0007 - this.log(`\u001B]1337;AddAnnotation=${text.length}|${annotation}\u0007${text}`) - } else { - this.log(text) - } - }, - - async flush() { - await timeout(flush(), 10_000) - }, + }, + + log(format?: string, ...args: string[]) { + this.info(format || '', ...args) + }, + + url(text: string, uri: string, params = {}) { + const supports = require('supports-hyperlinks') + if (supports.stdout) { + this.log(hyperlinker(text, uri, params)) + } else { + this.log(uri) + } + }, + + annotation(text: string, annotation: string) { + const supports = require('supports-hyperlinks') + if (supports.stdout) { + // \u001b]8;;https://google.com\u0007sometext\u001b]8;;\u0007 + this.log(`\u001B]1337;AddAnnotation=${text.length}|${annotation}\u0007${text}`) + } else { + this.log(text) + } + }, + + async flush() { + await timeout(flush(), 10_000) + }, + } } -export const cli = ux export { - config, - ActionBase, - UxConfig, - ExitError, - IPromptOptions, Table, } +export namespace CliUx { + export const cli = ux +} + const cliuxProcessExitHandler = async () => { try { - await ux.done() + await CliUx.ux.done() } catch (error) { // tslint:disable no-console console.error(error) diff --git a/src/cli-ux/prompt.ts b/src/cli-ux/prompt.ts index ab4958622..d8ab06c73 100644 --- a/src/cli-ux/prompt.ts +++ b/src/cli-ux/prompt.ts @@ -1,18 +1,19 @@ import * as Errors from '../../src/errors' import * as chalk from 'chalk' - -import config from './config' +import * as config from './config' import deps from './deps' -export interface IPromptOptions { - prompt?: string; - type?: 'normal' | 'mask' | 'hide' | 'single'; - timeout?: number; - /** - * Requires user input if true, otherwise allows empty input - */ - required?: boolean; - default?: string; +export namespace CliUx { + export interface IPromptOptions { + prompt?: string; + type?: 'normal' | 'mask' | 'hide' | 'single'; + timeout?: number; + /** + * Requires user input if true, otherwise allows empty input + */ + required?: boolean; + default?: string; + } } interface IPromptConfig { @@ -80,7 +81,7 @@ function replacePrompt(prompt: string) { deps.ansiEscapes.cursorDown(1) + deps.ansiEscapes.cursorLeft + deps.ansiEscapes.cursorShow) } -function _prompt(name: string, inputOptions: Partial = {}): Promise { +function _prompt(name: string, inputOptions: Partial = {}): Promise { const prompt = getPrompt(name, inputOptions.type, inputOptions.default) const options: IPromptConfig = { isTTY: Boolean(process.env.TERM !== 'dumb' && process.stdin.isTTY), @@ -116,52 +117,54 @@ function _prompt(name: string, inputOptions: Partial = {}): Prom } } -/** - * prompt for input - * @param name - prompt text - * @param options - @see IPromptOptions - * @returns void - */ -export function prompt(name: string, options: IPromptOptions = {}) { - return config.action.pauseAsync(() => { - return _prompt(name, options) - }, chalk.cyan('?')) -} +export namespace CliUx { + /** + * prompt for input + * @param name - prompt text + * @param options - @see IPromptOptions + * @returns void + */ + export function prompt(name: string, options: CliUx.IPromptOptions = {}) { + return config.CliUx.config.action.pauseAsync(() => { + return _prompt(name, options) + }, chalk.cyan('?')) + } + + /** + * confirmation prompt (yes/no) + * @param message - confirmation text + * @returns Promise + */ + export function confirm(message: string): Promise { + return config.CliUx.config.action.pauseAsync(async () => { + const confirm = async (): Promise => { + const response = (await _prompt(message)).toLowerCase() + if (['n', 'no'].includes(response)) return false + if (['y', 'yes'].includes(response)) return true + return confirm() + } -/** - * confirmation prompt (yes/no) - * @param message - confirmation text - * @returns Promise - */ -export function confirm(message: string): Promise { - return config.action.pauseAsync(async () => { - const confirm = async (): Promise => { - const response = (await _prompt(message)).toLowerCase() - if (['n', 'no'].includes(response)) return false - if (['y', 'yes'].includes(response)) return true return confirm() - } + }, chalk.cyan('?')) + } - return confirm() - }, chalk.cyan('?')) -} + /** + * "press anykey to continue" + * @param message - optional message to display to user + * @returns Promise + */ + export async function anykey(message?: string): Promise { + const tty = Boolean(process.stdin.setRawMode) + if (!message) { + message = tty ? + `Press any key to continue or ${chalk.yellow('q')} to exit` : + `Press enter to continue or ${chalk.yellow('q')} to exit` + } -/** - * "press anykey to continue" - * @param message - optional message to display to user - * @returns Promise - */ -export async function anykey(message?: string): Promise { - const tty = Boolean(process.stdin.setRawMode) - if (!message) { - message = tty ? - `Press any key to continue or ${chalk.yellow('q')} to exit` : - `Press enter to continue or ${chalk.yellow('q')} to exit` + const char = await prompt(message, {type: 'single', required: false}) + if (tty) process.stderr.write('\n') + if (char === 'q') Errors.error('quit') + if (char === '\u0003') Errors.error('ctrl-c') + return char } - - const char = await prompt(message, {type: 'single', required: false}) - if (tty) process.stderr.write('\n') - if (char === 'q') Errors.error('quit') - if (char === '\u0003') Errors.error('ctrl-c') - return char } diff --git a/src/cli-ux/styled/json.ts b/src/cli-ux/styled/json.ts index eec1748cd..5ad9e0ecb 100644 --- a/src/cli-ux/styled/json.ts +++ b/src/cli-ux/styled/json.ts @@ -2,16 +2,16 @@ import * as chalk from 'chalk' -import {cli} from '..' +import {CliUx} from '..' export default function styledJSON(obj: any) { const json = JSON.stringify(obj, null, 2) if (!chalk.level) { - cli.info(json) + CliUx.cli.info(json) return } const cardinal = require('cardinal') const theme = require('cardinal/themes/jq') - cli.info(cardinal.highlight(json, {json: true, theme})) + CliUx.cli.info(cardinal.highlight(json, {json: true, theme})) } diff --git a/src/command.ts b/src/command.ts index 10c17bcd3..79ae63566 100644 --- a/src/command.ts +++ b/src/command.ts @@ -1,7 +1,7 @@ import {fileURLToPath} from 'url' import {format, inspect} from 'util' -import {cli} from './cli-ux' +import {CliUx} from './' import {Config} from './config' import * as Interfaces from './interfaces' import * as Errors from './errors' @@ -163,7 +163,7 @@ export default abstract class Command { } if (result && this.jsonEnabled()) { - cli.styledJSON(this.toSuccessJson(result)) + CliUx.cli.styledJSON(this.toSuccessJson(result)) } return result @@ -227,12 +227,12 @@ export default abstract class Command { protected async catch(err: Record): Promise { process.exitCode = process.exitCode ?? err.exitCode ?? 1 if (this.jsonEnabled()) { - cli.styledJSON(this.toErrorJson(err)) + CliUx.cli.styledJSON(this.toErrorJson(err)) } else { if (!err.message) throw err try { const chalk = require('chalk') - cli.action.stop(chalk.bold.red('!')) + CliUx.cli.action.stop(chalk.bold.red('!')) } catch {} throw err diff --git a/test/cli-ux/fancy.ts b/test/cli-ux/fancy.ts index ea217fe5c..38e705558 100644 --- a/test/cli-ux/fancy.ts +++ b/test/cli-ux/fancy.ts @@ -2,7 +2,7 @@ import {expect, fancy as base, FancyTypes} from 'fancy-test' import * as fs from 'fs-extra' import * as path from 'path' -import {cli} from '../../src/cli-ux' +import {CliUx} from '../../src/cli-ux' export { expect, @@ -20,5 +20,5 @@ export const fancy = base chalk.level = 0 }) .finally(async () => { - await cli.done() + await CliUx.cli.done() }) diff --git a/test/cli-ux/index.test.ts b/test/cli-ux/index.test.ts index 81f6b0d52..6d49da3cd 100644 --- a/test/cli-ux/index.test.ts +++ b/test/cli-ux/index.test.ts @@ -1,4 +1,4 @@ -import {ux} from '../../src/cli-ux' +import {CliUx} from '../../src/cli-ux' import {expect, fancy} from './fancy' const hyperlinker = require('hyperlinker') @@ -7,7 +7,7 @@ describe('url', () => { fancy .env({FORCE_HYPERLINK: '1'}, {clear: true}) .stdout() - .do(() => ux.url('sometext', 'https://google.com')) + .do(() => CliUx.ux.url('sometext', 'https://google.com')) .it('renders hyperlink', async ({stdout}) => { expect(stdout).to.equal('sometext\n') }) diff --git a/test/cli-ux/prompt.test.ts b/test/cli-ux/prompt.test.ts index 2d1ff3e2a..d79c5dec4 100644 --- a/test/cli-ux/prompt.test.ts +++ b/test/cli-ux/prompt.test.ts @@ -2,7 +2,7 @@ import * as chai from 'chai' const expect = chai.expect -import {cli} from '../../src/cli-ux' +import {CliUx} from '../../src/cli-ux' import {fancy} from './fancy' @@ -11,11 +11,11 @@ describe('prompt', () => { .stdout() .stderr() .end('requires input', async () => { - const promptPromise = cli.prompt('Require input?') + const promptPromise = CliUx.cli.prompt('Require input?') process.stdin.emit('data', '') process.stdin.emit('data', 'answer') const answer = await promptPromise - await cli.done() + await CliUx.cli.done() expect(answer).to.equal('answer') }) @@ -24,9 +24,9 @@ describe('prompt', () => { .stderr() .stdin('y') .end('confirm', async () => { - const promptPromise = cli.confirm('yes/no?') + const promptPromise = CliUx.cli.confirm('yes/no?') const answer = await promptPromise - await cli.done() + await CliUx.cli.done() expect(answer).to.equal(true) }) @@ -35,9 +35,9 @@ describe('prompt', () => { .stderr() .stdin('n') .end('confirm', async () => { - const promptPromise = cli.confirm('yes/no?') + const promptPromise = CliUx.cli.confirm('yes/no?') const answer = await promptPromise - await cli.done() + await CliUx.cli.done() expect(answer).to.equal(false) }) @@ -46,9 +46,9 @@ describe('prompt', () => { .stderr() .stdin('x') .end('gets anykey', async () => { - const promptPromise = cli.anykey() + const promptPromise = CliUx.cli.anykey() const answer = await promptPromise - await cli.done() + await CliUx.cli.done() expect(answer).to.equal('x') }) @@ -56,12 +56,12 @@ describe('prompt', () => { .stdout() .stderr() .end('does not require input', async () => { - const promptPromise = cli.prompt('Require input?', { + const promptPromise = CliUx.cli.prompt('Require input?', { required: false, }) process.stdin.emit('data', '') const answer = await promptPromise - await cli.done() + await CliUx.cli.done() expect(answer).to.equal('') }) @@ -69,7 +69,7 @@ describe('prompt', () => { .stdout() .stderr() .it('timeouts with no input', async () => { - await expect(cli.prompt('Require input?', {timeout: 1})) + await expect(CliUx.cli.prompt('Require input?', {timeout: 1})) .to.eventually.be.rejectedWith('Prompt timeout') }) }) diff --git a/test/cli-ux/styled/object.test.ts b/test/cli-ux/styled/object.test.ts index 21819d527..0b6e672df 100644 --- a/test/cli-ux/styled/object.test.ts +++ b/test/cli-ux/styled/object.test.ts @@ -1,12 +1,12 @@ import {expect, fancy} from 'fancy-test' -import {cli} from '../../../src/cli-ux' +import {CliUx} from '../../../src/cli-ux' describe('styled/object', () => { fancy .stdout() .end('shows a table', output => { - cli.styledObject([ + CliUx.cli.styledObject([ {foo: 1, bar: 1}, {foo: 2, bar: 2}, {foo: 3, bar: 3}, diff --git a/test/cli-ux/styled/progress.test.ts b/test/cli-ux/styled/progress.test.ts index 76efc58f8..507effd9a 100644 --- a/test/cli-ux/styled/progress.test.ts +++ b/test/cli-ux/styled/progress.test.ts @@ -1,13 +1,13 @@ import {expect, fancy} from 'fancy-test' // import {BarType, Progress} from '../../src/progress' -import {cli} from '../../../src/cli-ux' +import {CliUx} from '../../../src/cli-ux' describe('progress', () => { // single bar fancy .end('single bar has default settings', _ => { - const b1 = cli.progress({format: 'Example 1: Progress {bar} | {percentage}%'}) + const b1 = CliUx.cli.progress({format: 'Example 1: Progress {bar} | {percentage}%'}) expect(b1.options.format).to.contain('Example 1: Progress') expect(b1.bars).to.not.have }) @@ -15,7 +15,7 @@ describe('progress', () => { // testing no settings passed, default settings created fancy .end('single bar, no bars array', _ => { - const b1 = cli.progress({}) + const b1 = CliUx.cli.progress({}) expect(b1.options.format).to.contain('progress') expect(b1.bars).to.not.have expect(b1.options.noTTYOutput).to.not.be.null @@ -23,7 +23,7 @@ describe('progress', () => { // testing getProgressBar returns correct type fancy .end('typeof progress bar is object', _ => { - const b1 = cli.progress({format: 'Example 1: Progress {bar} | {percentage}%'}) + const b1 = CliUx.cli.progress({format: 'Example 1: Progress {bar} | {percentage}%'}) expect(typeof (b1)).to.equal('object') }) }) diff --git a/test/cli-ux/styled/table.test.ts b/test/cli-ux/styled/table.test.ts index 3f9636211..0898eae18 100644 --- a/test/cli-ux/styled/table.test.ts +++ b/test/cli-ux/styled/table.test.ts @@ -1,6 +1,6 @@ import {expect, fancy} from 'fancy-test' -import {cli} from '../../../src/cli-ux' +import {CliUx} from '../../../src/cli-ux' /* eslint-disable camelcase */ const apps = [ @@ -71,13 +71,13 @@ const extendedHeader = `ID Name${ws.padEnd(14)}Web url${ws.padEnd(34)}Stack${ws describe('styled/table', () => { fancy .end('export flags and display()', () => { - expect(typeof (cli.table.flags())).to.eq('object') - expect(typeof (cli.table)).to.eq('function') + expect(typeof (CliUx.cli.table.flags())).to.eq('object') + expect(typeof (CliUx.cli.table)).to.eq('function') }) fancy .end('has optional flags', _ => { - const flags = cli.table.flags() + const flags = CliUx.cli.table.flags() expect(flags.columns).to.exist expect(flags.sort).to.exist expect(flags.filter).to.exist @@ -91,7 +91,7 @@ describe('styled/table', () => { fancy .stdout() .end('displays table', output => { - cli.table(apps, columns) + CliUx.cli.table(apps, columns) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} ─── ─────────────────${ws} 123 supertable-test-1${ws} @@ -102,14 +102,14 @@ describe('styled/table', () => { fancy .stdout() .end('use header value for id', output => { - cli.table(apps, columns) + CliUx.cli.table(apps, columns) expect(output.stdout.slice(1, 3)).to.equal('ID') }) fancy .stdout() .end('shows extended columns/uses get() for value', output => { - cli.table(apps, columns, {extended: true}) + CliUx.cli.table(apps, columns, {extended: true}) expect(output.stdout).to.equal(`${ws}${extendedHeader} ─── ───────────────── ──────────────────────────────────────── ─────────${ws} 123 supertable-test-1 https://supertable-test-1.herokuapp.com/ heroku-16${ws} @@ -121,14 +121,14 @@ describe('styled/table', () => { fancy .stdout() .end('shows extended columns', output => { - cli.table(apps, columns, {extended: true}) + CliUx.cli.table(apps, columns, {extended: true}) expect(output.stdout).to.contain(extendedHeader) }) fancy .stdout() .end('shows title with divider', output => { - cli.table(apps, columns, {title: 'testing'}) + CliUx.cli.table(apps, columns, {title: 'testing'}) expect(output.stdout).to.equal(`testing ======================= | ID Name${ws.padEnd(14)} @@ -140,7 +140,7 @@ describe('styled/table', () => { fancy .stdout() .end('skips header', output => { - cli.table(apps, columns, {'no-header': true}) + CliUx.cli.table(apps, columns, {'no-header': true}) expect(output.stdout).to.equal(` 123 supertable-test-1${ws} 321 supertable-test-2${ws}\n`) }) @@ -148,7 +148,7 @@ describe('styled/table', () => { fancy .stdout() .end('only displays given columns', output => { - cli.table(apps, columns, {columns: 'id'}) + CliUx.cli.table(apps, columns, {columns: 'id'}) expect(output.stdout).to.equal(` ID${ws}${ws} ───${ws} 123${ws} @@ -158,7 +158,7 @@ describe('styled/table', () => { fancy .stdout() .end('outputs in csv', output => { - cli.table(apps, columns, {output: 'csv'}) + CliUx.cli.table(apps, columns, {output: 'csv'}) expect(output.stdout).to.equal(`ID,Name 123,supertable-test-1 321,supertable-test-2\n`) @@ -167,7 +167,7 @@ describe('styled/table', () => { fancy .stdout() .end('outputs in csv with escaped values', output => { - cli.table([ + CliUx.cli.table([ { id: '123\n2', name: 'supertable-test-1', @@ -195,7 +195,7 @@ describe('styled/table', () => { fancy .stdout() .end('outputs in csv without headers', output => { - cli.table(apps, columns, {output: 'csv', 'no-header': true}) + CliUx.cli.table(apps, columns, {output: 'csv', 'no-header': true}) expect(output.stdout).to.equal(`123,supertable-test-1 321,supertable-test-2\n`) }) @@ -203,7 +203,7 @@ describe('styled/table', () => { fancy .stdout() .end('outputs in csv with alias flag', output => { - cli.table(apps, columns, {csv: true}) + CliUx.cli.table(apps, columns, {csv: true}) expect(output.stdout).to.equal(`ID,Name 123,supertable-test-1 321,supertable-test-2\n`) @@ -212,7 +212,7 @@ describe('styled/table', () => { fancy .stdout() .end('outputs in json', output => { - cli.table(apps, columns, {output: 'json'}) + CliUx.cli.table(apps, columns, {output: 'json'}) expect(output.stdout).to.equal(`[ { "id": "123", @@ -229,7 +229,7 @@ describe('styled/table', () => { fancy .stdout() .end('outputs in yaml', output => { - cli.table(apps, columns, {output: 'yaml'}) + CliUx.cli.table(apps, columns, {output: 'yaml'}) expect(output.stdout).to.equal(`- id: '123' name: supertable-test-1 - id: '321' @@ -241,7 +241,7 @@ describe('styled/table', () => { fancy .stdout() .end('sorts by property', output => { - cli.table(apps, columns, {sort: '-name'}) + CliUx.cli.table(apps, columns, {sort: '-name'}) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} ─── ─────────────────${ws} 321 supertable-test-2${ws} @@ -251,7 +251,7 @@ describe('styled/table', () => { fancy .stdout() .end('filters by property & value (partial string match)', output => { - cli.table(apps, columns, {filter: 'id=123'}) + CliUx.cli.table(apps, columns, {filter: 'id=123'}) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} ─── ─────────────────${ws} 123 supertable-test-1${ws}\n`) @@ -261,7 +261,7 @@ describe('styled/table', () => { .stdout() .end('does not truncate', output => { const three = {...apps[0], id: '0'.repeat(80), name: 'supertable-test-3'} - cli.table([...apps, three], columns, {filter: 'id=0', 'no-truncate': true}) + CliUx.cli.table([...apps, three], columns, {filter: 'id=0', 'no-truncate': true}) expect(output.stdout).to.equal(` ID${ws.padEnd(78)} Name${ws.padEnd(14)} ${''.padEnd(three.id.length, '─')} ─────────────────${ws} ${three.id} supertable-test-3${ws}\n`) @@ -271,14 +271,14 @@ describe('styled/table', () => { describe('#flags', () => { fancy .end('includes only flags', _ => { - const flags = cli.table.flags({only: 'columns'}) + const flags = CliUx.cli.table.flags({only: 'columns'}) expect(flags.columns).to.be.a('object') expect((flags as any).sort).to.be.undefined }) fancy .end('excludes except flags', _ => { - const flags = cli.table.flags({except: 'columns'}) + const flags = CliUx.cli.table.flags({except: 'columns'}) expect((flags as any).columns).to.be.undefined expect(flags.sort).to.be.a('object') }) @@ -288,7 +288,7 @@ describe('styled/table', () => { fancy .stdout() .end('ignores header case', output => { - cli.table(apps, columns, {columns: 'iD,Name', filter: 'nAMe=supertable-test', sort: '-ID'}) + CliUx.cli.table(apps, columns, {columns: 'iD,Name', filter: 'nAMe=supertable-test', sort: '-ID'}) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} ─── ─────────────────${ws} 321 supertable-test-2${ws} @@ -309,7 +309,7 @@ describe('styled/table', () => { } /* eslint-enable camelcase */ - cli.table([...apps, app3 as any], columns, {sort: '-ID'}) + CliUx.cli.table([...apps, app3 as any], columns, {sort: '-ID'}) expect(output.stdout).to.equal(` ID Name${ws.padEnd(14)} ─── ─────────────────${ws} 456 supertable-test${ws.padEnd(3)} diff --git a/test/cli-ux/styled/tree.test.ts b/test/cli-ux/styled/tree.test.ts index cb602495c..7a4026442 100644 --- a/test/cli-ux/styled/tree.test.ts +++ b/test/cli-ux/styled/tree.test.ts @@ -1,16 +1,16 @@ import {expect, fancy} from 'fancy-test' -import {cli} from '../../../src/cli-ux' +import {CliUx} from '../../../src/cli-ux' describe('styled/tree', () => { fancy .stdout() .end('shows the tree', output => { - const tree = cli.tree() + const tree = CliUx.cli.tree() tree.insert('foo') tree.insert('bar') - const subtree = cli.tree() + const subtree = CliUx.cli.tree() subtree.insert('qux') tree.nodes.bar.insert('baz', subtree)