Skip to content

Commit

Permalink
chore: wrap cli-ux exported members in namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
peternhale committed Jan 18, 2022
1 parent 263ff55 commit 905e5d5
Show file tree
Hide file tree
Showing 17 changed files with 421 additions and 414 deletions.
304 changes: 153 additions & 151 deletions src/cli-ux/action/base.ts
Original file line number Diff line number Diff line change
@@ -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<any>, 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<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 = 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])
}
}
}
6 changes: 3 additions & 3 deletions src/cli-ux/action/simple.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
7 changes: 3 additions & 4 deletions src/cli-ux/action/spinner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading

0 comments on commit 905e5d5

Please sign in to comment.