// prune-nested — the bare-build's smart nested-node_modules prune.
//
// Regression: it deleted a nested copy whenever the BUNDLE ROOT had the same
// version, ignoring an INTERMEDIATE node_modules that would shadow the root
// with a different version. That deleted compact-encoding@3.2.0 from under
// pgh/node_modules/hypercore (root also had 3.2.0), and hypercore then fell
// through to pgh/node_modules/compact-encoding@2.19.2 → DECODING_ERROR. The
// fix resolves against the NEAREST ancestor, not the root.
import test from 'brittle'
import fs from '#node:fs'
import os from '#node:os'
import path from '#node:path'
import { pruneNestedSmart, indexTopLevel } from '../scripts/lib/prune-nested.js'
function tmp () { return fs.mkdtempSync(path.join(os.tmpdir(), 'pgh-prune-')) }
function mkpkg (nmDir, name, version) {
const p = path.join(nmDir, name)
fs.mkdirSync(p, { recursive: true })
fs.writeFileSync(path.join(p, 'package.json'), JSON.stringify({ name, version }))
return p
}
function nmOf (pkgDir) { const p = path.join(pkgDir, 'node_modules'); fs.mkdirSync(p, { recursive: true }); return p }
const has = (...p) => fs.existsSync(path.join(...p))
const ver = (...p) => JSON.parse(fs.readFileSync(path.join(...p, 'package.json'), 'utf8')).version
test('KEEPS a deeply-nested copy an intermediate shadows (the exact bug)', (t) => {
const root = tmp()
const nm = path.join(root, 'node_modules'); fs.mkdirSync(nm, { recursive: true })
mkpkg(nm, 'compact-encoding', '3.2.0') // bundle root: ce 3.2.0
const pghNm = nmOf(mkpkg(nm, 'pgh', '0.1.0'))
mkpkg(pghNm, 'compact-encoding', '2.19.2') // intermediate: ce 2.19.2
const hcNm = nmOf(mkpkg(pghNm, 'hypercore', '11.33.1'))
mkpkg(hcNm, 'compact-encoding', '3.2.0') // deep: ce 3.2.0 (== root, != intermediate)
pruneNestedSmart(nm)
// Must be KEPT: hypercore's nearest ce ancestor is pgh's 2.19.2, so deleting
// its own 3.2.0 would mis-resolve to 2.19.2. (Old code deleted this.)
t.ok(has(hcNm, 'compact-encoding'), 'deeply-nested ce kept')
t.is(ver(hcNm, 'compact-encoding'), '3.2.0', 'and it is the 3.2.0 hypercore needs')
t.ok(has(pghNm, 'compact-encoding'), 'intermediate ce 2.19.2 kept (differs from root)')
fs.rmSync(root, { recursive: true, force: true })
})
test('DELETES a true hoist-dup (nearest ancestor is the root, same version)', (t) => {
const root = tmp()
const nm = path.join(root, 'node_modules'); fs.mkdirSync(nm, { recursive: true })
mkpkg(nm, 'b4a', '1.6.7')
const fooNm = nmOf(mkpkg(nm, 'foo', '1.0.0'))
mkpkg(fooNm, 'b4a', '1.6.7') // same as root, no intermediate
const stats = pruneNestedSmart(nm)
t.absent(has(fooNm, 'b4a'), 'true hoist-dup removed')
t.is(stats.dupsRemoved, 1)
t.ok(has(nm, 'b4a'), 'root copy retained')
fs.rmSync(root, { recursive: true, force: true })
})
test('KEEPS a version-conflict nested copy (differs from the only ancestor)', (t) => {
const root = tmp()
const nm = path.join(root, 'node_modules'); fs.mkdirSync(nm, { recursive: true })
mkpkg(nm, 'bar', '2.0.0')
const fooNm = nmOf(mkpkg(nm, 'foo', '1.0.0'))
mkpkg(fooNm, 'bar', '1.0.0') // differs from root 2.0.0
const stats = pruneNestedSmart(nm)
t.ok(has(fooNm, 'bar'), 'version-conflict copy kept')
t.is(stats.nestedKept, 1)
fs.rmSync(root, { recursive: true, force: true })
})
test('three-level chain: middle differs, deepest matches root → keep deepest', (t) => {
const root = tmp()
const nm = path.join(root, 'node_modules'); fs.mkdirSync(nm, { recursive: true })
mkpkg(nm, 'dep', '3.0.0') // root
const aNm = nmOf(mkpkg(nm, 'a', '1.0.0'))
mkpkg(aNm, 'dep', '2.0.0') // a/nm: dep 2.0.0
const bNm = nmOf(mkpkg(aNm, 'b', '1.0.0'))
mkpkg(bNm, 'dep', '3.0.0') // a/b/nm: dep 3.0.0 (== root, != a's 2.0.0)
pruneNestedSmart(nm)
t.ok(has(bNm, 'dep'), 'deepest kept — nearest ancestor (a) has 2.0.0, not 3.0.0')
t.ok(has(aNm, 'dep'), 'middle kept — differs from root')
fs.rmSync(root, { recursive: true, force: true })
})
test('scoped packages: hoist-dup removed', (t) => {
const root = tmp()
const nm = path.join(root, 'node_modules'); fs.mkdirSync(nm, { recursive: true })
mkpkg(nm, '@scope/x', '1.0.0')
const fooNm = nmOf(mkpkg(nm, 'foo', '1.0.0'))
mkpkg(fooNm, '@scope/x', '1.0.0') // same as root
const stats = pruneNestedSmart(nm)
t.absent(has(fooNm, '@scope', 'x'), 'scoped hoist-dup removed')
t.is(indexTopLevel(nm).get('@scope/x'), '1.0.0', 'indexTopLevel reads scoped names')
fs.rmSync(root, { recursive: true, force: true })
})