Skip to content

Commit 6c8163c

Browse files
authored
fix: support singular 'catalog' field in pnpm-workspace.yaml (#1572)
1 parent 37cb5fd commit 6c8163c

File tree

2 files changed

+147
-5
lines changed

2 files changed

+147
-5
lines changed

src/lib/getAllPackages.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import findPackage from './findPackage'
1212
import loadPackageInfoFromFile from './loadPackageInfoFromFile'
1313
import programError from './programError'
1414

15-
type PnpmWorkspaces = string[] | { packages: string[]; catalogs?: Index<Index<VersionSpec>> }
15+
type PnpmWorkspaces =
16+
| string[]
17+
| { packages: string[]; catalog?: Index<VersionSpec>; catalogs?: Index<Index<VersionSpec>> }
1618

1719
const globOptions: GlobOptions = {
1820
ignore: ['**/node_modules/**'],
@@ -37,8 +39,14 @@ const readCatalogDependencies = async (options: Options, pkgPath: string): Promi
3739
// Read from pnpm-workspace.yaml if the package manager is pnpm
3840
if (options.packageManager === 'pnpm') {
3941
const pnpmWorkspaces = await readPnpmWorkspaces(pkgPath)
40-
if (pnpmWorkspaces && !Array.isArray(pnpmWorkspaces) && pnpmWorkspaces.catalogs) {
41-
Object.assign(catalogDependencies, ...Object.values(pnpmWorkspaces.catalogs))
42+
if (pnpmWorkspaces && !Array.isArray(pnpmWorkspaces)) {
43+
// Handle both singular 'catalog' and plural 'catalogs'
44+
if (pnpmWorkspaces.catalog) {
45+
Object.assign(catalogDependencies, pnpmWorkspaces.catalog)
46+
}
47+
if (pnpmWorkspaces.catalogs) {
48+
Object.assign(catalogDependencies, ...Object.values(pnpmWorkspaces.catalogs))
49+
}
4250
}
4351
}
4452

@@ -225,7 +233,7 @@ async function getAllPackages(options: Options): Promise<[PackageInfo[], string[
225233
// Read catalog dependencies first so we can resolve references
226234
let catalogPackageInfo: PackageInfo | null = null
227235

228-
if (options.workspaces) {
236+
if (useWorkspaces) {
229237
const { pkgPath: workspacePkgPath } = await findPackage({
230238
...options,
231239
packageFile: rootPackageFile,

test/workspaces.test.ts

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ describe('workspaces', () => {
497497
}
498498
})
499499

500-
it('update pnpm catalog dependencies from pnpm-workspace.yaml', async () => {
500+
it('update pnpm catalog dependencies from pnpm-workspace.yaml (named catalogs)', async () => {
501501
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'npm-check-updates-'))
502502
try {
503503
const pkgDataRoot = JSON.stringify({
@@ -560,6 +560,140 @@ catalogs:
560560
await fs.rm(tempDir, { recursive: true, force: true })
561561
}
562562
})
563+
564+
it('update pnpm catalog dependencies from pnpm-workspace.yaml (singular catalog)', async () => {
565+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'npm-check-updates-'))
566+
try {
567+
const pkgDataRoot = JSON.stringify({
568+
dependencies: {
569+
'ncu-test-v2': '1.0.0',
570+
},
571+
})
572+
573+
const pnpmWorkspaceData = `packages:
574+
- 'packages/**'
575+
576+
catalog:
577+
ncu-test-v2: '1.0.0'
578+
ncu-test-tag: '1.0.0'
579+
`
580+
581+
// write root package file and pnpm-workspace.yaml
582+
await fs.writeFile(path.join(tempDir, 'package.json'), pkgDataRoot, 'utf-8')
583+
await fs.writeFile(path.join(tempDir, 'pnpm-workspace.yaml'), pnpmWorkspaceData, 'utf-8')
584+
await fs.writeFile(path.join(tempDir, 'pnpm-lock.yaml'), '', 'utf-8')
585+
586+
// create workspace package
587+
await fs.mkdir(path.join(tempDir, 'packages/a'), { recursive: true })
588+
await fs.writeFile(
589+
path.join(tempDir, 'packages/a/package.json'),
590+
JSON.stringify({
591+
dependencies: {
592+
'ncu-test-tag': 'catalog:',
593+
},
594+
}),
595+
'utf-8',
596+
)
597+
598+
const { stdout, stderr } = await spawn(
599+
'node',
600+
[bin, '--jsonAll', '--workspaces'],
601+
{ rejectOnError: false },
602+
{ cwd: tempDir },
603+
)
604+
605+
// Assert no errors and valid output
606+
stderr.should.be.empty
607+
stdout.should.not.be.empty
608+
609+
const output = JSON.parse(stdout)
610+
611+
// Should include catalog updates
612+
output.should.deep.equal({
613+
'pnpm-workspace.yaml': {
614+
packages: ['packages/**'],
615+
catalog: { 'ncu-test-v2': '2.0.0', 'ncu-test-tag': '1.1.0' },
616+
},
617+
'package.json': { dependencies: { 'ncu-test-v2': '2.0.0' } },
618+
'packages/a/package.json': { dependencies: { 'ncu-test-tag': 'catalog:' } },
619+
})
620+
} finally {
621+
await fs.rm(tempDir, { recursive: true, force: true })
622+
}
623+
})
624+
625+
it('update pnpm catalog with --workspace flag (specific workspace)', async () => {
626+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'npm-check-updates-'))
627+
try {
628+
const pkgDataRoot = JSON.stringify({
629+
workspaces: ['packages/*'],
630+
})
631+
632+
const pnpmWorkspaceData = `packages:
633+
- 'packages/*'
634+
635+
catalog:
636+
ncu-test-v2: '1.0.0'
637+
`
638+
639+
// write root package file and pnpm-workspace.yaml
640+
await fs.writeFile(path.join(tempDir, 'package.json'), pkgDataRoot, 'utf-8')
641+
await fs.writeFile(path.join(tempDir, 'pnpm-workspace.yaml'), pnpmWorkspaceData, 'utf-8')
642+
await fs.writeFile(path.join(tempDir, 'pnpm-lock.yaml'), '', 'utf-8')
643+
644+
// create workspace packages
645+
await fs.mkdir(path.join(tempDir, 'packages/a'), { recursive: true })
646+
await fs.writeFile(
647+
path.join(tempDir, 'packages/a/package.json'),
648+
JSON.stringify({
649+
name: 'a',
650+
dependencies: {
651+
'ncu-test-v2': 'catalog:',
652+
},
653+
}),
654+
'utf-8',
655+
)
656+
657+
await fs.mkdir(path.join(tempDir, 'packages/b'), { recursive: true })
658+
await fs.writeFile(
659+
path.join(tempDir, 'packages/b/package.json'),
660+
JSON.stringify({
661+
name: 'b',
662+
dependencies: {
663+
'ncu-test-tag': '1.0.0',
664+
},
665+
}),
666+
'utf-8',
667+
)
668+
669+
const { stdout, stderr } = await spawn(
670+
'node',
671+
[bin, '--jsonAll', '--workspace', 'a'],
672+
{ rejectOnError: false },
673+
{ cwd: tempDir },
674+
)
675+
676+
// Assert no errors and valid output
677+
stderr.should.be.empty
678+
stdout.should.not.be.empty
679+
680+
const output = JSON.parse(stdout)
681+
682+
// Should include catalog updates even when using --workspace (not --workspaces)
683+
output.should.have.property('pnpm-workspace.yaml')
684+
output['pnpm-workspace.yaml'].should.deep.equal({
685+
packages: ['packages/*'],
686+
catalog: { 'ncu-test-v2': '2.0.0' },
687+
})
688+
output.should.have.property('packages/a/package.json')
689+
output['packages/a/package.json'].should.deep.equal({
690+
name: 'a',
691+
dependencies: { 'ncu-test-v2': 'catalog:' },
692+
})
693+
} finally {
694+
await fs.rm(tempDir, { recursive: true, force: true })
695+
}
696+
})
563697
})
564698

565699
describe('bun', () => {

0 commit comments

Comments
 (0)