Skip to content

Commit a03098c

Browse files
committed
refactor(@angular/cli): add custom parser for bun pm ls
Bun does not support JSON output for the `pm ls` command. This commit introduces a custom parser to interpret Bun's tree-like output format, allowing the package manager abstraction to correctly discover installed dependencies when using Bun.
1 parent 4330501 commit a03098c

File tree

3 files changed

+100
-3
lines changed

3 files changed

+100
-3
lines changed

packages/angular/cli/src/package-managers/package-manager-descriptor.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Logger } from './logger';
1717
import { PackageManifest, PackageMetadata } from './package-metadata';
1818
import { InstalledPackage } from './package-tree';
1919
import {
20+
parseBunDependencies,
2021
parseNpmLikeDependencies,
2122
parseNpmLikeError,
2223
parseNpmLikeManifest,
@@ -261,10 +262,10 @@ export const SUPPORTED_PACKAGE_MANAGERS = {
261262
copyConfigFromProject: true,
262263
getRegistryOptions: (registry: string) => ({ args: ['--registry', registry] }),
263264
versionCommand: ['--version'],
264-
listDependenciesCommand: ['pm', 'ls', '--json'],
265+
listDependenciesCommand: ['pm', 'ls'],
265266
getManifestCommand: ['pm', 'view', '--json'],
266267
outputParsers: {
267-
listDependencies: parseNpmLikeDependencies,
268+
listDependencies: parseBunDependencies,
268269
getRegistryManifest: parseNpmLikeManifest,
269270
getRegistryMetadata: parseNpmLikeMetadata,
270271
getError: parseNpmLikeError,

packages/angular/cli/src/package-managers/parsers.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,3 +521,57 @@ export function parseYarnClassicError(output: string, logger?: Logger): ErrorInf
521521

522522
return null;
523523
}
524+
525+
/**
526+
* Parses the output of `bun pm ls`.
527+
*
528+
* Bun does not support JSON output for `pm ls`. The output is a tree structure:
529+
* ```
530+
* /path/to/project node_modules (1084)
531+
* ├── @angular/[email protected]
532+
* ├── rxjs @7.8.2
533+
* └── zone.js @0.15.1
534+
* ```
535+
*
536+
* @param stdout The standard output of the command.
537+
* @param logger An optional logger instance.
538+
* @returns A map of package names to their installed package details.
539+
*/
540+
export function parseBunDependencies(
541+
stdout: string,
542+
logger?: Logger,
543+
): Map<string, InstalledPackage> {
544+
logger?.debug('Parsing Bun dependency list...');
545+
logStdout(stdout, logger);
546+
547+
const dependencies = new Map<string, InstalledPackage>();
548+
if (!stdout) {
549+
return dependencies;
550+
}
551+
552+
const lines = stdout.split('\n');
553+
// Skip the first line (project info)
554+
for (let i = 1; i < lines.length; i++) {
555+
const line = lines[i].trim();
556+
if (!line) {
557+
continue;
558+
}
559+
560+
// Remove tree structure characters
561+
const cleanLine = line.replace(/^[]\s*/, '');
562+
563+
// Parse name and version
564+
// Scoped: @angular/[email protected]
565+
// Unscoped: rxjs @7.8.2
566+
const match = cleanLine.match(/^(.+?)\s?@([^@\s]+)$/);
567+
if (match) {
568+
const name = match[1];
569+
const version = match[2];
570+
dependencies.set(name, { name, version });
571+
}
572+
}
573+
574+
logger?.debug(` Found ${dependencies.size} dependencies.`);
575+
576+
return dependencies;
577+
}

packages/angular/cli/src/package-managers/parsers_spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import { parseNpmLikeError, parseNpmLikeManifest, parseYarnClassicError } from './parsers';
9+
import {
10+
parseBunDependencies,
11+
parseNpmLikeError,
12+
parseNpmLikeManifest,
13+
parseYarnClassicError,
14+
} from './parsers';
1015

1116
describe('parsers', () => {
1217
describe('parseNpmLikeError', () => {
@@ -128,4 +133,41 @@ describe('parsers', () => {
128133
expect(error).toBeNull();
129134
});
130135
});
136+
137+
describe('parseBunDependencies', () => {
138+
it('should parse bun pm ls output', () => {
139+
const stdout = `
140+
/tmp/angular-cli-e2e-PiL5n3/e2e-test/assets/19.0-project-1767113081927 node_modules (1084)
141+
├── @angular-devkit/[email protected]
142+
├── @angular/[email protected]
143+
├── jasmine-core @5.6.0
144+
├── rxjs @7.8.2
145+
└── zone.js @0.15.1
146+
`.trim();
147+
148+
const deps = parseBunDependencies(stdout);
149+
expect(deps.size).toBe(5);
150+
expect(deps.get('@angular-devkit/build-angular')).toEqual({
151+
name: '@angular-devkit/build-angular',
152+
version: '20.3.13',
153+
});
154+
expect(deps.get('@angular/cli')).toEqual({ name: '@angular/cli', version: '20.3.13' });
155+
expect(deps.get('jasmine-core')).toEqual({ name: 'jasmine-core', version: '5.6.0' });
156+
expect(deps.get('rxjs')).toEqual({ name: 'rxjs', version: '7.8.2' });
157+
expect(deps.get('zone.js')).toEqual({ name: 'zone.js', version: '0.15.1' });
158+
});
159+
160+
it('should return empty map for empty stdout', () => {
161+
expect(parseBunDependencies('').size).toBe(0);
162+
});
163+
164+
it('should skip lines that do not match the pattern', () => {
165+
const stdout = `
166+
project node_modules
167+
├── invalid-line
168+
└── another-invalid
169+
`.trim();
170+
expect(parseBunDependencies(stdout).size).toBe(0);
171+
});
172+
});
131173
});

0 commit comments

Comments
 (0)