From b4fe1ba9c32515b595b389eba9c07aa6a6b094a1 Mon Sep 17 00:00:00 2001 From: Arthur031221 Date: Sat, 10 Oct 2026 12:07:31 +0800 Subject: [PATCH] fix(@angular/cli): resolve the package name inside npm workspace members Inside an npm workspace member, `npm pkg get name` prints an object keyed by the workspace name instead of a JSON string. `getCurrentPackageName` returned that object, so the workspace lookup in `parseNpmLikeDependencies` never matched and `ng update` saw no dependencies for the member. The name is now only taken from the command output when it is a string. Otherwise it is read from the package.json of the current directory. Fixes #32787 --- .../src/package-managers/package-manager.ts | 7 ++- .../package-managers/package-manager_spec.ts | 51 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/angular/cli/src/package-managers/package-manager.ts b/packages/angular/cli/src/package-managers/package-manager.ts index 234b54851232..5ad5fa773404 100644 --- a/packages/angular/cli/src/package-managers/package-manager.ts +++ b/packages/angular/cli/src/package-managers/package-manager.ts @@ -397,7 +397,12 @@ export class PackageManager { try { const { stdout } = await this.#run(this.descriptor.getPackageNameCommand); if (stdout) { - return JSON.parse(stdout); + const name = JSON.parse(stdout); + + // In a workspace member npm prints an object keyed by workspace name instead of a string. + if (typeof name === 'string') { + return name; + } } } catch { // Fall back to reading file if command fails diff --git a/packages/angular/cli/src/package-managers/package-manager_spec.ts b/packages/angular/cli/src/package-managers/package-manager_spec.ts index d500a435d0df..ff52bf00a995 100644 --- a/packages/angular/cli/src/package-managers/package-manager_spec.ts +++ b/packages/angular/cli/src/package-managers/package-manager_spec.ts @@ -378,6 +378,57 @@ describe('PackageManager', () => { }); }); + describe('getCurrentPackageName', () => { + it('should return the name printed by the package manager', async () => { + const pm = new PackageManager(host, '/repo/app', descriptor); + runCommandSpy.and.resolveTo({ stdout: '"app"', stderr: '' }); + const readFileSpy = spyOn(host, 'readFile'); + + expect(await pm.getCurrentPackageName()).toBe('app'); + expect(readFileSpy).not.toHaveBeenCalled(); + }); + + it('should read package.json when npm prints an object keyed by workspace', async () => { + const pm = new PackageManager(host, '/repo/apps/app', descriptor); + // Inside a workspace member, `npm pkg get name` prints `{ "app": "app" }` instead of `"app"`. + runCommandSpy.and.resolveTo({ stdout: '{\n "app": "app"\n}', stderr: '' }); + const readFileSpy = spyOn(host, 'readFile').and.resolveTo('{"name": "app"}'); + + expect(await pm.getCurrentPackageName()).toBe('app'); + expect(readFileSpy).toHaveBeenCalledWith(jasmine.stringMatching(/package\.json$/)); + }); + }); + + describe('getProjectDependencies', () => { + it('should list the dependencies of an npm workspace member', async () => { + const pm = new PackageManager(host, '/repo/apps/app', descriptor); + runCommandSpy.and.callFake((_binary: string, args: readonly string[]) => + Promise.resolve({ + stdout: + args[0] === 'pkg' + ? '{\n "app": "app"\n}' + : JSON.stringify({ + name: 'monorepo', + dependencies: { + app: { + version: '0.0.0', + resolved: 'file:../apps/app', + dependencies: { '@angular/core': { version: '22.0.0' } }, + }, + }, + }), + stderr: '', + }), + ); + spyOn(host, 'readFile').and.resolveTo('{"name": "app"}'); + + const dependencies = await pm.getProjectDependencies(); + + expect([...dependencies.keys()]).toEqual(['@angular/core']); + expect(dependencies.get('@angular/core')?.version).toBe('22.0.0'); + }); + }); + describe('initializationError', () => { it('should throw initializationError when running commands', async () => { const error = new Error('Not installed');