import { format } from 'util' ; import chalk from 'chalk' ; import wrapAnsi from 'wrap-ansi' ; import { pipe } from '@k88/pipe-compose' ; import env from 'flex-plugins-utils-env' ; import stringWidth from 'string-width' ; import columnify from './columnify' ; type Level = 'info' | 'error' | 'warn' ; type Color = 'red' | 'yellow' | 'green' | 'cyan' | 'magenta' ; interface ColumnsOptions { indent ? : boolean ; } export type LogLevels = 'debug' | 'info' | 'warning' | 'error' | 'trace' | 'success' ; interface LogArg { color ? : Color ; level : Level ; args : string [ ] ; } interface LoggerOptions { isQuiet ? : boolean ; isDebug ? : boolean ; isTrace ? : boolean ; markdown ? : boolean ; } interface Formatter { [ key ] : { openChars : string ; closeChars : string ; render : ( msg : string ) => string ; } ; } const DefaultWrapOptions = { hard : true } ; export const coloredStrings = { dim : chalk . dim , bold : chalk . bold , italic : chalk . italic , code : chalk . magenta , link : chalk . cyan , info : chalk . blue , success : chalk . green , warning : chalk . yellow , error : chalk . red , headline : chalk . bold . green , name : chalk . bold . magenta , digit : chalk . magenta , } ; /**
 * The Logger class
 */ export class Logger { private static formatter : Formatter = { dim : { openChars : '\\.{2}' , closeChars : '\\.{2}' , render : coloredStrings . dim , } , bold : { openChars : '\\*{2}' , closeChars : '\\*{2}' , render : coloredStrings . bold , } , italic : { openChars : '\\*' , closeChars : '\\*' , render : coloredStrings . italic , } , code : { openChars : '\\{{2}' , closeChars : '\\}{2}' , render : coloredStrings . code , } , link : { openChars : '\\[{2}' , closeChars : '\\]{2}' , render : coloredStrings . link , } , info : { openChars : '@{2}' , closeChars : '@{2}' , render : coloredStrings . info , } , success : { openChars : '\\+{2}' , closeChars : '\\+{2}' , render : coloredStrings . success , } , warning : { openChars : '\\!{2}' , closeChars : '\\!{2}' , render : coloredStrings . warning , } , error : { openChars : '\\-{2}' , closeChars : '\\-{2}' , render : coloredStrings . error , } , } ; private readonly options : LoggerOptions ; constructor ( options ? ) { this . options = options || { } ; } param args public debug = ( ... args ) : void => { if ( this . isDebug ( ) ) { this . _log ( { level : 'info' , args } ) ; } } ; param args public trace = ( ... args ) : void => { if ( this . isTrace ( ) ) { this . _log ( { level : 'info' , args } ) ; } } ; param args public info = ( ... args ) : void => { this . _log ( { level : 'info' , args } ) ; } ; param args public success = ( ... args ) : void => { this . _log ( { level : 'info' , color : 'green' , args } ) ; } ; param args public error = ( ... args ) : void => { this . _log ( { level : 'error' , color : 'red' , args } ) ; } ; param args public warning = ( ... args ) : void => { this . _log ( { level : 'warn' , color : 'yellow' , args } ) ; } ; param args public notice = ( ... args ) : void => { this . _log ( { level : 'info' , color : 'magenta' , args } ) ; } ; param lines param options public columns = ( lines , options ? ) : void => { const minWidths = Array ( lines [ 0 ] . length ) . fill ( 0 ) ; lines . forEach ( ( line ) => { line . forEach ( ( entry , index ) => { minWidths [ index ] = Math . max ( minWidths [ index ] , stringWidth ( entry ) ) ; } ) ; } ) ; const data = lines . map ( ( line ) => { return line . reduce ( ( accum , entry , index ) => { accum [ `entry${ index }` ] = entry ; return accum ; } , { } ) ; } ) ; const config = minWidths . reduce ( ( accum , minWidth , index ) => { if ( index < minWidths . length - 1 ) { minWidth += 5 ; } accum [ `entry${ index }` ] = { minWidth } ; return accum ; } , { } ) ; let cols = columnify ( data , { showHeaders : false , config } ) ; if ( options ?. indent ) { cols = cols . split ( '\n' ) . map ( ( entry ) => `\t${ entry }` ) . join ( '\n' ) ; } this . _log ( { level : 'info' , args : [ cols ] } ) ; } ; param lines public newline = ( lines = 1 ) : void => { for ( let i = 0 ; i < lines ; i ++ ) { this . info ( ) ; } } ; param command param args public installInfo = ( command , ... args ) : void => { this . info ( '\t' , chalk . cyan ( command ) , ... args ) ; } ; /**
     * Clears the terminal either if forced is provided, or if persist_terminal env is not set
     */ public clearTerminal = ( forced = false ) : void => { if ( forced || ! env . isTerminalPersisted ( ) ) { process . stdout . write ( env . isWin32 ( ) ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H' ) ; } } ; param msg public markdown = ( msg ? ) : string | undefined => { if ( ! msg || msg === '' ) { return msg ; } for ( const key in Logger . formatter ) { if ( Logger . formatter . hasOwnProperty ( key ) ) { const formatter = Logger . formatter [ key ] ; const regex = new RegExp ( `${ formatter . openChars }(.*?)${ formatter . closeChars }` ) ; const match = msg . match ( regex ) ; if ( match ) { const replace = match [ 0 ] . replace ( new RegExp ( formatter . openChars , 'g' ) , '' ) . replace ( new RegExp ( formatter . closeChars , 'g' ) , '' ) ; return this . markdown ( msg . replace ( regex , formatter . render ( replace ) ) ) ; } } } return msg . replace ( /\\/g , '' ) ; } ; param args private private _log = ( args ) : void => { if ( ! this . isQuiet ( ) || args . level === 'error' || this . isDebug ( ) ) { const log = console [ args . level ] ; const color = args . color ? chalk [ args . color ] : ( msg ) => msg ; const msg = format . apply ( { } , args . args as any ) ; pipe ( msg , color , this . markdown , log ) ; } } ; /**
     * Checks whether the logger is set for debug mode
     */ private isDebug = ( ) : boolean => { if ( 'isDebug' in this . options ) { return this . options . isDebug || false ; } return env . isDebug ( ) ; } ; /**
     * Checks whether the logger is set for trace mode
     */ private isTrace = ( ) : boolean => { if ( 'isTrace' in this . options ) { return this . options . isTrace || false ; } return env . isTrace ( ) ; } ; private isQuiet = ( ) : boolean => { if ( 'isQuiet' in this . options ) { return this . options . isQuiet || false ; } return env . isQuiet ( ) ; } ; } param input param columns param options const wrap = ( input , columns , options = DefaultWrapOptions ) : string => { return wrapAnsi ( input , columns , options ) ; } ; /**
 * The default logger will use environment variables to determine behavior.
 * You can create an instance to overwrite default behavior.
 */ export const _logger = new Logger ( ) ; const { debug , info , warning , error , trace , success , newline , notice , installInfo , clearTerminal , markdown , columns } = _logger ; export default { debug , info , warning , error , trace , success , newline , notice , installInfo , clearTerminal , markdown , wrap , columns , colors : chalk , coloredStrings , } ;