Skip to content

Commit 5c6cacb

Browse files
committed
refactor(@angular/cli): separate the version command's analysis logic from logging
Refactors the `version` command's output generation for better readability and maintainability. This also allows other parts of the CLI to gather the version info programmatically, if needed. The key changes include: - Extracting the output formatting logic from the `run` method into a new private `formatVersionInfo` function. - Introducing a `VersionInfo` interface to provide a clear data structure for the version details. - Moving the ASCII art logo to a module-level constant to separate presentation from logic. - Relocating the `gatherVersionInfo` function, `VersionInfo` interface, and related constants to a new `version-info.ts` file to better organize the code.
1 parent 31f2384 commit 5c6cacb

File tree

2 files changed

+269
-147
lines changed

2 files changed

+269
-147
lines changed

packages/angular/cli/src/commands/version/cli.ts

Lines changed: 102 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,30 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import nodeModule from 'node:module';
10-
import { resolve } from 'node:path';
11-
import { Argv } from 'yargs';
9+
import type { Argv } from 'yargs';
1210
import { CommandModule, CommandModuleImplementation } from '../../command-builder/command-module';
1311
import { colors } from '../../utilities/color';
1412
import { RootCommands } from '../command-config';
15-
16-
interface PartialPackageInfo {
17-
name: string;
18-
version: string;
19-
dependencies?: Record<string, string>;
20-
devDependencies?: Record<string, string>;
21-
}
13+
import { VersionInfo, gatherVersionInfo } from './version-info';
2214

2315
/**
24-
* Major versions of Node.js that are officially supported by Angular.
16+
* The Angular CLI logo, displayed as ASCII art.
2517
*/
26-
const SUPPORTED_NODE_MAJORS = [20, 22, 24];
27-
28-
const PACKAGE_PATTERNS = [
29-
/^@angular\/.*/,
30-
/^@angular-devkit\/.*/,
31-
/^@ngtools\/.*/,
32-
/^@schematics\/.*/,
33-
/^rxjs$/,
34-
/^typescript$/,
35-
/^ng-packagr$/,
36-
/^webpack$/,
37-
/^zone\.js$/,
38-
];
18+
const ASCII_ART = `
19+
_ _ ____ _ ___
20+
/ \\ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
21+
/ △ \\ | '_ \\ / _\` | | | | |/ _\` | '__| | | | | | |
22+
/ ___ \\| | | | (_| | |_| | | (_| | | | |___| |___ | |
23+
/_/ \\_\\_| |_|\\__, |\\__,_|_|\\__,_|_| \\____|_____|___|
24+
|___/
25+
`
26+
.split('\n')
27+
.map((x) => colors.red(x))
28+
.join('\n');
3929

30+
/**
31+
* The command-line module for the `ng version` command.
32+
*/
4033
export default class VersionCommandModule
4134
extends CommandModule
4235
implements CommandModuleImplementation
@@ -46,146 +39,108 @@ export default class VersionCommandModule
4639
describe = 'Outputs Angular CLI version.';
4740
longDescriptionPath?: string | undefined;
4841

42+
/**
43+
* Builds the command-line options for the `ng version` command.
44+
* @param localYargs The `yargs` instance to configure.
45+
* @returns The configured `yargs` instance.
46+
*/
4947
builder(localYargs: Argv): Argv {
5048
return localYargs;
5149
}
5250

51+
/**
52+
* The main execution logic for the `ng version` command.
53+
*/
5354
async run(): Promise<void> {
54-
const { packageManager, logger, root } = this.context;
55-
const localRequire = nodeModule.createRequire(resolve(__filename, '../../../'));
56-
// Trailing slash is used to allow the path to be treated as a directory
57-
const workspaceRequire = nodeModule.createRequire(root + '/');
58-
59-
const cliPackage: PartialPackageInfo = localRequire('./package.json');
60-
let workspacePackage: PartialPackageInfo | undefined;
61-
try {
62-
workspacePackage = workspaceRequire('./package.json');
63-
} catch {}
64-
65-
const [nodeMajor] = process.versions.node.split('.').map((part) => Number(part));
66-
const unsupportedNodeVersion = !SUPPORTED_NODE_MAJORS.includes(nodeMajor);
67-
68-
const packageNames = new Set(
69-
Object.keys({
70-
...cliPackage.dependencies,
71-
...cliPackage.devDependencies,
72-
...workspacePackage?.dependencies,
73-
...workspacePackage?.devDependencies,
74-
}),
75-
);
76-
77-
const versions: Record<string, string> = {};
78-
for (const name of packageNames) {
79-
if (PACKAGE_PATTERNS.some((p) => p.test(name))) {
80-
versions[name] = this.getVersion(name, workspaceRequire, localRequire);
81-
}
82-
}
55+
const { logger } = this.context;
56+
const versionInfo = gatherVersionInfo(this.context);
57+
const {
58+
ngCliVersion,
59+
nodeVersion,
60+
unsupportedNodeVersion,
61+
packageManagerName,
62+
packageManagerVersion,
63+
os,
64+
arch,
65+
versions,
66+
} = versionInfo;
67+
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, '');
8374

84-
const ngCliVersion = cliPackage.version;
85-
let angularCoreVersion = '';
86-
const angularSameAsCore: string[] = [];
87-
88-
if (workspacePackage) {
89-
// Filter all angular versions that are the same as core.
90-
angularCoreVersion = versions['@angular/core'];
91-
if (angularCoreVersion) {
92-
for (const [name, version] of Object.entries(versions)) {
93-
if (version === angularCoreVersion && name.startsWith('@angular/')) {
94-
angularSameAsCore.push(name.replace(/^@angular\//, ''));
95-
delete versions[name];
96-
}
97-
}
75+
const angularPackages = this.formatAngularPackages(versionInfo);
76+
const packageTable = this.formatPackageTable(versions);
9877

99-
// Make sure we list them in alphabetical order.
100-
angularSameAsCore.sort();
101-
}
102-
}
103-
104-
const namePad = ' '.repeat(
105-
Object.keys(versions).sort((a, b) => b.length - a.length)[0].length + 3,
106-
);
107-
const asciiArt = `
108-
_ _ ____ _ ___
109-
/ \\ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
110-
/ △ \\ | '_ \\ / _\` | | | | |/ _\` | '__| | | | | | |
111-
/ ___ \\| | | | (_| | |_| | | (_| | | | |___| |___ | |
112-
/_/ \\_\\_| |_|\\__, |\\__,_|_|\\__,_|_| \\____|_____|___|
113-
|___/
114-
`
115-
.split('\n')
116-
.map((x) => colors.red(x))
117-
.join('\n');
118-
119-
logger.info(asciiArt);
120-
logger.info(
121-
`
122-
Angular CLI: ${ngCliVersion}
123-
Node: ${process.versions.node}${unsupportedNodeVersion ? ' (Unsupported)' : ''}
124-
Package Manager: ${packageManager.name} ${packageManager.version ?? '<error>'}
125-
OS: ${process.platform} ${process.arch}
126-
127-
Angular: ${angularCoreVersion}
128-
... ${angularSameAsCore
129-
.reduce<string[]>((acc, name) => {
130-
// Perform a simple word wrap around 60.
131-
if (acc.length == 0) {
132-
return [name];
133-
}
134-
const line = acc[acc.length - 1] + ', ' + name;
135-
if (line.length > 60) {
136-
acc.push(name);
137-
} else {
138-
acc[acc.length - 1] = line;
139-
}
140-
141-
return acc;
142-
}, [])
143-
.join('\n... ')}
144-
145-
Package${namePad.slice(7)}Version
146-
-------${namePad.replace(/ /g, '-')}------------------
147-
${Object.keys(versions)
148-
.map((module) => `${module}${namePad.slice(module.length)}${versions[module]}`)
149-
.sort()
150-
.join('\n')}
151-
`.replace(/^ {6}/gm, ''),
152-
);
78+
logger.info([ASCII_ART, header, angularPackages, packageTable].join('\n\n'));
15379

15480
if (unsupportedNodeVersion) {
15581
logger.warn(
156-
`Warning: The current version of Node (${process.versions.node}) is not supported by Angular.`,
82+
`Warning: The current version of Node (${nodeVersion}) is not supported by Angular.`,
15783
);
15884
}
15985
}
16086

161-
private getVersion(
162-
moduleName: string,
163-
workspaceRequire: NodeRequire,
164-
localRequire: NodeRequire,
165-
): string {
166-
let packageInfo: PartialPackageInfo | undefined;
167-
let cliOnly = false;
168-
169-
// Try to find the package in the workspace
170-
try {
171-
packageInfo = workspaceRequire(`${moduleName}/package.json`);
172-
} catch {}
173-
174-
// If not found, try to find within the CLI
175-
if (!packageInfo) {
176-
try {
177-
packageInfo = localRequire(`${moduleName}/package.json`);
178-
cliOnly = true;
179-
} catch {}
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>';
18096
}
18197

182-
// If found, attempt to get the version
183-
if (packageInfo) {
184-
try {
185-
return packageInfo.version + (cliOnly ? ' (cli-only)' : '');
186-
} catch {}
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+
117+
/**
118+
* Formats the package table section of the version output.
119+
* @param versions A map of package names to their versions.
120+
* @returns A string containing the formatted package table.
121+
*/
122+
private formatPackageTable(versions: Record<string, string>): string {
123+
const versionKeys = Object.keys(versions);
124+
if (versionKeys.length === 0) {
125+
return '';
187126
}
188127

189-
return '<error>';
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);
131+
132+
const tableHeader = `${header}${namePad}Version`;
133+
const separator = '-'.repeat(tableHeader.length);
134+
135+
const tableRows = versionKeys
136+
.map((module) => {
137+
const padding = ' '.repeat(maxNameLength - module.length + 3);
138+
139+
return `${module}${padding}${versions[module]}`;
140+
})
141+
.sort()
142+
.join('\n');
143+
144+
return `${tableHeader}\n${separator}\n${tableRows}`;
190145
}
191146
}

0 commit comments

Comments
 (0)