From 0ea6c766b0110ba6111f6ba4bcaf3a6ea58e9fc7 Mon Sep 17 00:00:00 2001 From: Max Black Date: Fri, 14 Nov 2025 13:51:22 -0800 Subject: [PATCH 1/2] fix: prevent crash when expanding directories.bin without filesystem path Fixes npm/cli#8722 When normalizing a package manifest fetched from a registry (without a filesystem path), attempting to expand 'directories.bin' would crash with ERR_INVALID_ARG_TYPE because pkg.path is undefined. This adds a check to only expand directories.bin when pkg.path exists, preventing the crash while maintaining correct behavior for packages with a filesystem location. Affects users with custom/private registries that don't pre-calculate the 'bin' field from 'directories.bin' in their package manifests. --- lib/normalize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/normalize.js b/lib/normalize.js index f65e6ad..0388220 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -474,7 +474,7 @@ async function asyncSteps (pkg, { steps, root, changes }) { } // expand "directories.bin" - if (steps.includes('binDir') && data.directories?.bin && !data.bin) { + if (steps.includes('binDir') && data.directories?.bin && !data.bin && pkg.path) { const binPath = secureAndUnixifyPath(data.directories.bin) const bins = await lazyLoadGlob()('**', { cwd: path.resolve(pkg.path, binPath) }) data.bin = bins.reduce((acc, binFile) => { From b544e4e1cc9bd47063e649ea23603af63f5ab5c7 Mon Sep 17 00:00:00 2001 From: Max Black Date: Mon, 17 Nov 2025 12:53:28 -0800 Subject: [PATCH 2/2] add test for directories.bin without filesystem path --- test/normalize.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/normalize.js b/test/normalize.js index 59fa2b3..ba298a6 100644 --- a/test/normalize.js +++ b/test/normalize.js @@ -172,6 +172,20 @@ for (const [name, testNormalize] of Object.entries(testMethods)) { t.strictSame(content.bin, { echo: 'bin/echo' }) }) + t.test('directories.bin without filesystem path', async t => { + const p = new Pkg() + p.fromContent({ + name: 'registry-test', + version: '1.0.0', + directories: { + bin: './bin', + }, + }) + await p.normalize({ steps: Pkg.normalizeSteps }) + t.notOk(p.content.bin, 'bin should not be expanded without filesystem path') + t.ok(p.content.directories.bin, 'directories.bin should be preserved') + }) + t.test('dedupe optional deps out of regular deps', async t => { t.test('choose optional deps in conflict, removing empty dependencies', async t => { const { content } = await testNormalize(t, ({