diff --git a/packages/angular/cli/src/commands/version/cli.ts b/packages/angular/cli/src/commands/version/cli.ts index 135358a4b7ba..9ba53a902ebd 100644 --- a/packages/angular/cli/src/commands/version/cli.ts +++ b/packages/angular/cli/src/commands/version/cli.ts @@ -45,24 +45,34 @@ export default class VersionCommandModule * @returns The configured `yargs` instance. */ builder(localYargs: Argv): Argv { - return localYargs; + return localYargs.option('json', { + describe: 'Outputs version information in JSON format.', + type: 'boolean', + }); } /** * The main execution logic for the `ng version` command. */ - async run(): Promise { + async run(options: { json?: boolean }): Promise { const { logger } = this.context; const versionInfo = gatherVersionInfo(this.context); + + if (options.json) { + // eslint-disable-next-line no-console + console.log(JSON.stringify(versionInfo, null, 2)); + + return; + } + const { - ngCliVersion, - nodeVersion, - unsupportedNodeVersion, - packageManagerName, - packageManagerVersion, - os, - arch, - versions, + cli: { version: ngCliVersion }, + system: { + node: { version: nodeVersion, unsupported: unsupportedNodeVersion }, + os: { platform: os, architecture: arch }, + packageManager: { name: packageManagerName, version: packageManagerVersion }, + }, + packages, } = versionInfo; const headerInfo = [ @@ -87,7 +97,7 @@ export default class VersionCommandModule ) .join('\n'); - const packageTable = this.formatPackageTable(versions); + const packageTable = this.formatPackageTable(packages); logger.info([ASCII_ART, header, packageTable].join('\n\n')); diff --git a/packages/angular/cli/src/commands/version/version-info.ts b/packages/angular/cli/src/commands/version/version-info.ts index c59a3b5728af..1e8b6b557514 100644 --- a/packages/angular/cli/src/commands/version/version-info.ts +++ b/packages/angular/cli/src/commands/version/version-info.ts @@ -23,14 +23,24 @@ interface PartialPackageInfo { * An object containing all the version information that will be displayed by the command. */ export interface VersionInfo { - ngCliVersion: string; - versions: Record; - unsupportedNodeVersion: boolean; - nodeVersion: string; - packageManagerName: string; - packageManagerVersion: string | undefined; - os: string; - arch: string; + cli: { + version: string; + }; + system: { + node: { + version: string; + unsupported: boolean; + }; + os: { + platform: string; + architecture: string; + }; + packageManager: { + name: string; + version: string | undefined; + }; + }; + packages: Record; } /** @@ -81,22 +91,32 @@ export function gatherVersionInfo(context: { }), ); - const versions: Record = {}; + const packages: Record = {}; for (const name of packageNames) { if (PACKAGE_PATTERNS.some((p) => p.test(name))) { - versions[name] = getVersion(name, workspaceRequire); + packages[name] = getVersion(name, workspaceRequire); } } return { - ngCliVersion: VERSION.full, - versions, - unsupportedNodeVersion, - nodeVersion: process.versions.node, - packageManagerName: context.packageManager.name, - packageManagerVersion: context.packageManager.version, - os: process.platform, - arch: process.arch, + cli: { + version: VERSION.full, + }, + system: { + node: { + version: process.versions.node, + unsupported: unsupportedNodeVersion, + }, + os: { + platform: process.platform, + architecture: process.arch, + }, + packageManager: { + name: context.packageManager.name, + version: context.packageManager.version, + }, + }, + packages, }; } diff --git a/tests/legacy-cli/e2e/utils/process.ts b/tests/legacy-cli/e2e/utils/process.ts index 3cd6c77bc187..cb542ff1ea0f 100644 --- a/tests/legacy-cli/e2e/utils/process.ts +++ b/tests/legacy-cli/e2e/utils/process.ts @@ -5,6 +5,7 @@ import { concat, defer, EMPTY, from, lastValueFrom, catchError, repeat } from 'r import { getGlobalVariable, getGlobalVariablesEnv } from './env'; import treeKill from 'tree-kill'; import { delimiter, join, resolve } from 'node:path'; +import { stripVTControlCharacters } from 'node:util'; interface ExecOptions { silent?: boolean; @@ -236,14 +237,14 @@ export async function waitForAnyProcessOutputToMatch( childProcess.stdout!.on('data', (data: Buffer) => { stdout += data.toString(); - if (stdout.match(match)) { + if (stripVTControlCharacters(stdout).match(match)) { resolve({ stdout, stderr }); } }); childProcess.stderr!.on('data', (data: Buffer) => { stderr += data.toString(); - if (stderr.match(match)) { + if (stripVTControlCharacters(stderr).match(match)) { resolve({ stdout, stderr }); } });