file copyright author Vivek M. Chawla <@VivekMChawla>
 *  summary description version license import * as _ from 'lodash' ; import { SfdxFalconDebug } from '../sfdx-falcon-debug' ; import { SfdxFalconError } from '../sfdx-falcon-error' ; const stripAnsi = require ( 'strip-ansi' ) ; const chalk = require ( 'chalk' ) ; const pad = require ( 'pad' ) ; const dbgNs = 'UTILITY:ux:' ; interface description export interface SfdxFalconKeyValueTableDataRow { option : string ; value : string ; height ? : number ; } type SfdxFalconTableData description export type SfdxFalconTableData = SfdxFalconKeyValueTableDataRow [ ] ; interface description export interface StatusMessage { title : string ; message : string ; type : 'error' | 'info' | 'success' | 'warning' ; } interface description export interface TableColumn { key : string ; label ? : string | ( ( ) => string ) ; width : number ; format ( value , row ) : string ; get ( row ) : string ; } interface description export interface TableColumnKey { key : string ; label ? : string | ( ( ) => string ) ; } interface description export interface TableOptions { columns : Partial < TableColumn > [ ] ; colSep : string ; headerAnsi : any ; after ( row , options ) : void ; printLine ( row ) : void ; printRow ( row ) : void ; printInverseRow ( row ) : void ; printHeader ( row ) : void ; } function param { StatusMessage } statusMessages param { number } padLength param { string } separator returns { void } description public export function printStatusMessage ( statusMessage , padLength = 0 , separator = ' : ' ) { if ( typeof statusMessage . title !== 'string' ) { throw new SfdxFalconError ( `Expected string for statusMessage.title but got '${ typeof statusMessage . title }'` , `TypeError` , `${ dbgNs }printStatusMessage` ) ; } if ( typeof statusMessage . message !== 'string' ) { throw new SfdxFalconError ( `Expected string for statusMessage.message but got '${ typeof statusMessage . message }'` , `TypeError` , `${ dbgNs }printStatusMessage` ) ; } if ( typeof separator !== 'string' ) { throw new SfdxFalconError ( `Expected string for separator but got '${ typeof separator }'` , `TypeError` , `${ dbgNs }printStatusMessage` ) ; } if ( isNaN ( padLength ) ) { padLength = 0 ; } switch ( statusMessage . type ) { case 'error' : console . log ( chalk `{bold.red ${ pad ( statusMessage . title , padLength ) + separator }}{bold ${ statusMessage . message }}` ) ; break ; case 'info' : console . log ( chalk `{bold ${ pad ( statusMessage . title , padLength ) + separator }}${ statusMessage . message }` ) ; break ; case 'success' : console . log ( chalk `{bold ${ pad ( statusMessage . title , padLength ) + separator }}{green ${ statusMessage . message }}` ) ; break ; case 'warning' : console . log ( chalk `{bold ${ pad ( statusMessage . title , padLength ) + separator }}{yellow ${ statusMessage . message }}` ) ; break ; default : throw new SfdxFalconError ( `Invalid setting for statusMessage.type: '${ statusMessage . type }'` , `InvalidMessageType` , `${ dbgNs }printStatusMessage` ) ; } } function param { StatusMessage [ ] } statusMessages param { number } padLength param { string } separator returns { void } description public export function printStatusMessages ( statusMessages , separator = ' : ' ) { if ( Array . isArray ( statusMessages ) === false ) { throw new SfdxFalconError ( `Expected array for statusMessages but got '${ typeof statusMessages }'` , `TypeError` , `${ dbgNs }printStatusMessages` ) ; } if ( typeof separator !== 'string' ) { throw new SfdxFalconError ( `Expected string for separator but got '${ typeof separator }'` , `TypeError` , `${ dbgNs }printStatusMessages` ) ; } let longestTitle = 0 ; for ( const statusMessage of statusMessages ) { if ( typeof statusMessage . title !== 'undefined' ) { longestTitle = Math . max ( statusMessage . title . length , longestTitle ) ; } } const statusHeader = pad ( 'Final Status' , longestTitle + separator . length - 1 ) ; console . log ( chalk `{inverse ${ statusHeader }}` ) ; for ( const statusMessage of statusMessages ) { printStatusMessage ( statusMessage , longestTitle , separator ) ; } } function param { SfdxFalconKeyValueTableDataRow [ ] } data param { Partial < TableOptions > } tableOptionsOverride returns { void } description private function renderTable ( data , tableOptionsOverride = { } ) { const tableOptions = { colSep : '  ' , after : ( ) => { } , headerAnsi : _ . identity , printLine : ( s ) => console . log ( s ) , printRow : function ( cells ) { this . printLine ( ( cells . join ( this . colSep ) as any ) . trimRight ( ) ) ; } , printInverseRow : function ( cells ) { for ( let i = 0 ; i < cells . length ; i ++ ) { process . stdout . write ( chalk . inverse ( cells [ i ] ) ) ; if ( i < cells . length - 1 ) { process . stdout . write ( this . colSep ) ; } } process . stdout . write ( '\n' ) ; } , printHeader : function ( cells ) { this . printRow ( cells . map ( _ . ary ( this . headerAnsi , 1 ) ) ) ; this . printRow ( cells . map ( hdr => hdr . replace ( /./g , '─' ) ) ) ; } , ... tableOptionsOverride , columns : ( tableOptionsOverride . columns || [ ] ) . map ( c => ( { format : ( value ) => { return value != null ? value . toString ( ) : '' ; } , width : 0 , label : function ( ) { return this . key ! . toString ( ) ; } , get : function ( row ) { let value ; const path = _ . result ( this , 'key' ) ; if ( ! path ) { value = row ; } else { value = _ . get ( row , path ) ; } return ( this . format as any ) ( value , row ) ; } , ... c } ) ) } ; SfdxFalconDebug . obj ( `${ dbgNs }renderTable:` , tableOptions , `tableOptions: ` ) ; function calcWidth ( cell ) { const lines = stripAnsi ( cell ) . split ( /[\r\n]+/ ) ; const lineLengths = lines . map ( _ . property ( 'length' ) ) ; return Math . max . apply ( Math , lineLengths ) ; } function pad ( string , length ) { const visibleLength = stripAnsi ( string ) . length ; const diff = length - visibleLength ; return string + ' ' . repeat ( Math . max ( 0 , diff ) ) ; } function render ( ) { let columns = tableOptions . columns as any ; if ( typeof columns [ 0 ] === 'string' ) { columns = ( columns as any ) . map ( ( key ) => ( { key : key } ) ) ; } for ( const row of data ) { row . height = 1 ; for ( const col of columns ) { const cell = col . get ( row as any ) ; col . width = Math . max ( ( _ . result ( col , 'label' ) as string ) . length , col . width || 0 , calcWidth ( cell ) ) ; row . height = Math . max ( row . height || 0 , cell . split ( /[\r\n]+/ ) . length ) ; } } if ( tableOptions . printHeader ) { tableOptions . printHeader ( columns . map ( col => { const label = _ . result ( col , 'label' ) as string ; return pad ( label , col . width || label . length ) ; } ) ) ; } function getNthLineOfCell ( n , row , col ) { const lines = col . get ( row ) . split ( /[\r\n]+/ ) ; return pad ( lines [ n ] || '' , col . width ) ; } for ( const row of data ) { for ( let i = 0 ; i < ( row . height || 0 ) ; i ++ ) { const cells = columns . map ( _ . partial ( getNthLineOfCell , i , row ) ) ; tableOptions . printRow ( cells ) ; } tableOptions . after ( row , tableOptions ) ; } } render ( ) ; } class description public export class SfdxFalconKeyValueTable { private tableColumnKeys : TableColumnKey [ ] ; private tableOptions : Partial < TableOptions > ; constructs param { TableColumnKey [ ] } tableColumnKeys param { Partial < TableOptions > } tableOptions param { boolean } debugMode description public constructor ( tableColumnKeys ? , tableOptions ? , debugMode ? ) { if ( typeof tableColumnKeys === 'undefined' || tableColumnKeys . length !== 2 ) { this . tableColumnKeys = [ { key : 'option' , label : 'OPTIONS' } , { key : 'value' , label : 'VALUES' } ] ; } this . tableOptions = { colSep : ' ' , printHeader : function ( cells ) { const headerCells = cells . map ( _ . ary ( this . headerAnsi , 1 ) ) ; for ( let i = 0 ; i < headerCells . length ; i ++ ) { process . stdout . write ( chalk . inverse ( headerCells [ i ] ) ) ; if ( i < headerCells . length - 1 ) { process . stdout . write ( this . colSep ) ; } } process . stdout . write ( '\n' ) ; } , printRow : function ( cells ) { process . stdout . write ( chalk . bold ( cells [ 0 ] ) ) ; process . stdout . write ( this . colSep ) ; process . stdout . write ( chalk . green ( cells [ 1 ] ) ) ; process . stdout . write ( '\n' ) ; } , ... tableOptions , columns : this . tableColumnKeys } ; } method param { SfdxFalconKeyValueTableDataRow [ ] } keyValueTableData returns { void } description public public render ( keyValueTableData ) { renderTable ( keyValueTableData , this . tableOptions ) ; } }