import Table , { HorizontalAlignment } from "cli-table3" ; import giturl from "git-url-parse" ; type PackageType = "dependencies" | "devDependencies" | "peerDependencies" | "optionalDependencies" | "bundledDependencies" | "bundleDependencies" | "shadow" ; export type PackageJsonByName = Map < string , PackageJson > ; export class CompareModel { public name : string ; public current : string ; public wanted : string ; public packageType : PackageType ; public repo ? : string ; public homepage ? : string ; public tags : Set < string > ; constructor ( project , o , n ) { this . name = o . name ; this . current = o . version ; this . wanted = n . version ; this . packageType = this . toPackageType ( this . name , project ) ; this . repo = this . toURL ( o . repository ) ; this . homepage = o . homepage ; this . tags = new Set ( ) ; } public rangeWanted ( ) { return this . versionRange ( this . current , this . wanted ) ; } public diffWantedURL ( ) { return this . diffURL ( this . wanted ) ; } protected toTag ( version ) { const v = `v${ version }` ; if ( this . tags . has ( v ) ) { return v ; } return this . tags . has ( version ) && version ; } protected diffURL ( to ) { if ( this . repo ) { if ( this . current === to ) { const tag = this . toTag ( this . current ) ; return tag && `${ this . repo }/tree/${ tag }` ; } const ft = this . toTag ( this . current ) ; const tt = this . toTag ( to ) ; return ft && tt && `${ this . repo }/compare/${ ft }...${ tt }` ; } return "" ; } protected versionRange ( current , to ) { if ( current === to ) { return current ; } return `${ current }...${ to }` ; } protected toPackageType ( name , pkg ) { const contains = ( n , d ? ) => d && d [ n ] ; if ( contains ( name , pkg . dependencies ) ) { return "dependencies" ; } if ( contains ( name , pkg . devDependencies ) ) { return "devDependencies" ; } if ( contains ( name , pkg . optionalDependencies ) ) { return "optionalDependencies" ; } const bundled = pkg . bundledDependencies ; if ( bundled && bundled . find ( ( n ) => name === n ) ) { return "bundledDependencies" ; } const bundle = pkg . bundleDependencies ; if ( bundle && bundle . find ( ( n ) => name === n ) ) { return "bundleDependencies" ; } return "shadow" ; } protected toURL ( repo ? ) { if ( typeof repo === "string" ) { if ( 2 === repo . split ( "/" ) . length ) { return `https://github.com/${ repo }` ; } } else if ( repo && repo . url ) { const u = giturl ( repo . url ) ; return u && u . toString ( "https" ) . replace ( /\.git$/ , "" ) ; } } } type ColumnRenderer = ( cm : CompareModel ) => string ; class Column { public name : string ; public layout : string ; public render : ColumnRenderer ; public cliLayout : HorizontalAlignment ; public cliRender : ColumnRenderer ; constructor ( name , layout , render , cliLayout , cliRender ) { this . name = name ; this . layout = layout ; this . render = render ; this . cliLayout = cliLayout ; this . cliRender = cliRender ; } } function makeColumns ( entries ) { const columns = [ ] ; columns . push ( new Column ( "Name" , ":---- " , ( cw ) => { return cw . homepage ? `[${ cw . name }](${ cw . homepage })` : `\`${ cw . name }\`` ; } , "left" , ( cw ) => cw . name ) ) ; columns . push ( new Column ( "Updating" , ":--------:" , ( cw ) => { const u = cw . diffWantedURL ( ) ; return u ? `[${ cw . rangeWanted ( ) }](${ u })` : cw . rangeWanted ( ) ; } , "center" , ( cw ) => cw . rangeWanted ( ) ) ) ; const depnames = [ "dependencies" , "devDependencies" , "peerDependencies" , "optionalDependencies" , "bundledDependencies" , "bundleDependencies" , "shadow" ] ; depnames . forEach ( ( n ) => { if ( entries . find ( ( v ) => v . packageType === n ) ) { const fn = ( cw ) => ( cw . packageType === n ? "*" : " " ) ; columns . push ( new Column ( n , ":-:" , fn , "center" , fn ) ) ; } } ) ; return columns ; } function headers ( columns ) { const a = columns . map ( ( col ) => col . name ) ; return `| ${ a . join ( " | " ) } |` ; } function layouts ( columns ) { const a = columns . map ( ( col ) => col . layout ) ; return `| ${ a . join ( " | " ) } |` ; } function rows ( columns , entries ) { return entries . map ( ( c ) => { const a = columns . map ( ( col ) => col . render ( c ) ) ; return `| ${ a . join ( " | " ) } |` ; } ) . join ( "\n" ) ; } export function toTextTable ( project , oldone , newone ) { const models = toCompareModels ( project , oldone , newone ) ; const columns = makeColumns ( models ) ; const table = new Table ( { head : columns . map ( ( col ) => col . name ) , chars : { top : "=" , "top-mid" : "=" , "top-left" : "" , "top-right" : "=" , bottom : "=" , "bottom-mid" : "=" , "bottom-left" : "" , "bottom-right" : "=" , left : "|" , "left-mid" : "" , mid : "-" , "mid-mid" : "-" , right : "|" , "right-mid" : "" , middle : "|" } , colAligns : columns . map ( ( col ) => col . cliLayout ) , style : { head : [ ] , "padding-left" : 1 , "padding-right" : 1 } } ) ; models . forEach ( ( c ) => { table . push ( columns . map ( ( col ) => col . cliRender ( c ) ) ) ; } ) ; return table . toString ( ) ; } export function toMarkdown ( project , oldone , newone ) { const models = toCompareModels ( project , oldone , newone ) ; const columns = makeColumns ( models ) ; return `${ headers ( columns ) }
${ layouts ( columns ) }
${ rows ( columns , models ) }` ; } function toCompareModels ( project , oldone , newone ) { return Array . from ( oldone . entries ( ) ) . filter ( ( [ name , o ] ) => { const n = newone . get ( name ) ; return n && n . version && n . version !== o . version ; } ) . map ( ( [ name , o ] ) => { const n = < PackageJson > newone . get ( name ) ; return new CompareModel ( project , o , n ) ; } ) ; }