diff --git a/.changeset/shy-weeks-raise.md b/.changeset/shy-weeks-raise.md new file mode 100644 index 0000000000..7fe0bc68d7 --- /dev/null +++ b/.changeset/shy-weeks-raise.md @@ -0,0 +1,6 @@ +--- +"@pnpm/plugin-commands-store": patch +"pnpm": patch +--- + +`pnpm store prune` should not fail if the dlx cache directory has files, not only directories [#10384](https://github.com/pnpm/pnpm/pull/10384) diff --git a/store/plugin-commands-store/src/cleanExpiredDlxCache.test.ts b/store/plugin-commands-store/src/cleanExpiredDlxCache.test.ts index 221f28d5b8..f1b448c4d7 100644 --- a/store/plugin-commands-store/src/cleanExpiredDlxCache.test.ts +++ b/store/plugin-commands-store/src/cleanExpiredDlxCache.test.ts @@ -192,3 +192,31 @@ test("cleanOrphans deletes dirs that don't contain `link` and subdirs that aren' // expecting directory that doesn't contain `link` to be deleted. expect(fs.existsSync(path.join(cacheDir, 'dlx', createCacheKey('bar')))).toBe(false) }) + +test('cleanExpiredDlxCache ignores files in the dlx cache directory', async () => { + prepareEmpty() + + const cacheDir = path.resolve('cache') + const dlxCacheMaxAge = 0 + const now = new Date() + + // Create a directory that should be cleaned + createSampleDlxCacheItem(cacheDir, 'foo', now, 1) + + // Create a file in the dlx directory (simulating the bug/noise) + const dlxDir = path.join(cacheDir, 'dlx') + fsOriginal.mkdirSync(dlxDir, { recursive: true }) + fsOriginal.writeFileSync(path.join(dlxDir, 'some-random-file'), 'hello') + + await cleanExpiredDlxCache({ + cacheDir, + dlxCacheMaxAge, + now, + }) + + // The directory should be gone (cleaned) + expect(fsOriginal.existsSync(path.join(dlxDir, createCacheKey('foo')))).toBeFalsy() + + // The file should still be there (ignored) + expect(fsOriginal.existsSync(path.join(dlxDir, 'some-random-file'))).toBeTruthy() +}) diff --git a/store/plugin-commands-store/src/cleanExpiredDlxCache.ts b/store/plugin-commands-store/src/cleanExpiredDlxCache.ts index 11cf0270df..c7af911f27 100644 --- a/store/plugin-commands-store/src/cleanExpiredDlxCache.ts +++ b/store/plugin-commands-store/src/cleanExpiredDlxCache.ts @@ -74,7 +74,16 @@ async function getStats (path: string): Promise { function readOptDir (dirPath: string): string[] | null { try { - return readdirSync(dirPath, 'utf-8') + const dirEntries: string[] = [] + for (const entry of readdirSync(dirPath, { + encoding: 'utf-8', + withFileTypes: true, + })) { + if (entry.isDirectory()) { + dirEntries.push(entry.name) + } + } + return dirEntries } catch (err) { if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') { return null