@@ -10,7 +10,7 @@ import type { Argv } from 'yargs';
1010import { CommandModule , CommandModuleImplementation } from '../../command-builder/command-module' ;
1111import { colors } from '../../utilities/color' ;
1212import { RootCommands } from '../command-config' ;
13- import { VersionInfo , gatherVersionInfo } from './version-info' ;
13+ import { gatherVersionInfo } from './version-info' ;
1414
1515/**
1616 * The Angular CLI logo, displayed as ASCII art.
@@ -45,37 +45,61 @@ export default class VersionCommandModule
4545 * @returns The configured `yargs` instance.
4646 */
4747 builder ( localYargs : Argv ) : Argv {
48- return localYargs ;
48+ return localYargs . option ( 'json' , {
49+ describe : 'Outputs version information in JSON format.' ,
50+ type : 'boolean' ,
51+ } ) ;
4952 }
5053
5154 /**
5255 * The main execution logic for the `ng version` command.
5356 */
54- async run ( ) : Promise < void > {
57+ async run ( options : { json ?: boolean } ) : Promise < void > {
5558 const { logger } = this . context ;
5659 const versionInfo = gatherVersionInfo ( this . context ) ;
60+
61+ if ( options . json ) {
62+ // eslint-disable-next-line no-console
63+ console . log ( JSON . stringify ( versionInfo , null , 2 ) ) ;
64+
65+ return ;
66+ }
67+
5768 const {
58- ngCliVersion,
59- nodeVersion,
60- unsupportedNodeVersion,
61- packageManagerName,
62- packageManagerVersion,
63- os,
64- arch,
65- versions,
69+ cli : { version : ngCliVersion } ,
70+ system : {
71+ node : { version : nodeVersion , unsupported : unsupportedNodeVersion } ,
72+ os : { platform : os , architecture : arch } ,
73+ packageManager : { name : packageManagerName , version : packageManagerVersion } ,
74+ } ,
75+ packages,
6676 } = versionInfo ;
6777
68- const header = `
69- Angular CLI: ${ ngCliVersion }
70- Node: ${ nodeVersion } ${ unsupportedNodeVersion ? ' (Unsupported)' : '' }
71- Package Manager: ${ packageManagerName } ${ packageManagerVersion ?? '<error>' }
72- OS: ${ os } ${ arch }
73- ` . replace ( / ^ { 6 } / gm, '' ) ;
78+ const headerInfo = [
79+ { label : 'Angular CLI' , value : ngCliVersion } ,
80+ {
81+ label : 'Node.js' ,
82+ value : `${ nodeVersion } ${ unsupportedNodeVersion ? colors . yellow ( ' (Unsupported)' ) : '' } ` ,
83+ } ,
84+ {
85+ label : 'Package Manager' ,
86+ value : `${ packageManagerName } ${ packageManagerVersion ?? '<error>' } ` ,
87+ } ,
88+ { label : 'Operating System' , value : `${ os } ${ arch } ` } ,
89+ ] ;
90+
91+ const maxHeaderLabelLength = Math . max ( ...headerInfo . map ( ( l ) => l . label . length ) ) ;
92+
93+ const header = headerInfo
94+ . map (
95+ ( { label, value } ) =>
96+ `${ colors . bold ( label ) } ` . padEnd ( maxHeaderLabelLength + 11 ) + `: ${ colors . cyan ( value ) } ` ,
97+ )
98+ . join ( '\n' ) ;
7499
75- const angularPackages = this . formatAngularPackages ( versionInfo ) ;
76- const packageTable = this . formatPackageTable ( versions ) ;
100+ const packageTable = this . formatPackageTable ( packages ) ;
77101
78- logger . info ( [ ASCII_ART , header , angularPackages , packageTable ] . join ( '\n\n' ) ) ;
102+ logger . info ( [ ASCII_ART , header , packageTable ] . join ( '\n\n' ) ) ;
79103
80104 if ( unsupportedNodeVersion ) {
81105 logger . warn (
@@ -84,36 +108,6 @@ export default class VersionCommandModule
84108 }
85109 }
86110
87- /**
88- * Formats the Angular packages section of the version output.
89- * @param versionInfo An object containing the version information.
90- * @returns A string containing the formatted Angular packages information.
91- */
92- private formatAngularPackages ( versionInfo : VersionInfo ) : string {
93- const { angularCoreVersion, angularSameAsCore } = versionInfo ;
94- if ( ! angularCoreVersion ) {
95- return 'Angular: <error>' ;
96- }
97-
98- const wrappedPackages = angularSameAsCore
99- . reduce < string [ ] > ( ( acc , name ) => {
100- if ( acc . length === 0 ) {
101- return [ name ] ;
102- }
103- const line = acc [ acc . length - 1 ] + ', ' + name ;
104- if ( line . length > 60 ) {
105- acc . push ( name ) ;
106- } else {
107- acc [ acc . length - 1 ] = line ;
108- }
109-
110- return acc ;
111- } , [ ] )
112- . join ( '\n... ' ) ;
113-
114- return `Angular: ${ angularCoreVersion } \n... ${ wrappedPackages } ` ;
115- }
116-
117111 /**
118112 * Formats the package table section of the version output.
119113 * @param versions A map of package names to their versions.
@@ -125,22 +119,33 @@ export default class VersionCommandModule
125119 return '' ;
126120 }
127121
128- const header = 'Package' ;
129- const maxNameLength = Math . max ( ...versionKeys . map ( ( key ) => key . length ) ) ;
130- const namePad = ' ' . repeat ( Math . max ( 0 , maxNameLength - header . length ) + 3 ) ;
122+ const nameHeader = 'Package' ;
123+ const versionHeader = 'Version' ;
131124
132- const tableHeader = `${ header } ${ namePad } Version` ;
133- const separator = '-' . repeat ( tableHeader . length ) ;
125+ const maxNameLength = Math . max ( nameHeader . length , ...versionKeys . map ( ( key ) => key . length ) ) ;
126+ const maxVersionLength = Math . max (
127+ versionHeader . length ,
128+ ...versionKeys . map ( ( key ) => versions [ key ] . length ) ,
129+ ) ;
134130
135131 const tableRows = versionKeys
136132 . map ( ( module ) => {
137- const padding = ' ' . repeat ( maxNameLength - module . length + 3 ) ;
133+ const name = module . padEnd ( maxNameLength ) ;
134+ const version = versions [ module ] ;
135+ const coloredVersion = version === '<error>' ? colors . red ( version ) : colors . cyan ( version ) ;
136+ const padding = ' ' . repeat ( maxVersionLength - version . length ) ;
138137
139- return `${ module } ${ padding } ${ versions [ module ] } ` ;
138+ return `│ ${ name } │ ${ coloredVersion } ${ padding } │ ` ;
140139 } )
141- . sort ( )
142- . join ( '\n' ) ;
140+ . sort ( ) ;
141+
142+ const top = `┌─${ '─' . repeat ( maxNameLength ) } ─┬─${ '─' . repeat ( maxVersionLength ) } ─┐` ;
143+ const header = `│ ${ nameHeader . padEnd ( maxNameLength ) } │ ${ versionHeader . padEnd (
144+ maxVersionLength ,
145+ ) } │`;
146+ const separator = `├─${ '─' . repeat ( maxNameLength ) } ─┼─${ '─' . repeat ( maxVersionLength ) } ─┤` ;
147+ const bottom = `└─${ '─' . repeat ( maxNameLength ) } ─┴─${ '─' . repeat ( maxVersionLength ) } ─┘` ;
143148
144- return ` ${ tableHeader } \n ${ separator } \n ${ tableRows } ` ;
149+ return [ top , header , separator , ... tableRows , bottom ] . join ( '\n' ) ;
145150 }
146151}
0 commit comments