#!/usr/bin/env node
// flatten-node-modules must keep pgh's per-package wiring alive.
//
// pgh materializes node_modules/<pkg> -> .pgh/<hash>/ and wires each package's
// own deps as RELATIVE links: .pgh/<hash>/node_modules/<dep> -> ../../<depHash>.
// Copying a package out of .pgh moves those links one or two levels, so they
// dangled (1310/1310 on stortron, 0.1217.0) and every nested require fell through
// to the top level. keet-core 4.22 then loaded the app's blind-peering 2.1.2
// instead of its own 2.9.3 and aborted on the first autobee room.
//
// Bare caches modules by REALPATH, so a hash must also load from ONE path:
// its flattened top-level copy if it has one, else .pgh/<hash>.
//
// node scripts/test/flatten-node-modules.test.cjs
'use strict'
const fs = require('fs')
const os = require('os')
const path = require('path')
const { execFileSync } = require('child_process')
let failed = 0
const ok = (c, m) => { console.log((c ? 'ok ' : 'FAIL ') + m); if (!c) failed++ }
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'flatten-test-'))
const nm = path.join(dir, 'node_modules')
const pgh = path.join(nm, '.pgh')
function pkg (hash, name, version) {
const d = path.join(pgh, hash)
fs.mkdirSync(path.join(d, 'node_modules'), { recursive: true })
fs.writeFileSync(path.join(d, 'package.json'), JSON.stringify({ name, version }))
fs.writeFileSync(path.join(d, 'index.js'), '')
}
// a package's own dep, wired exactly as pgh's materializer does
function wire (hash, depName, depHash) {
const link = path.join(pgh, hash, 'node_modules', depName)
fs.mkdirSync(path.dirname(link), { recursive: true })
const up = depName.includes('/') ? '../../../' : '../../'
fs.symlinkSync(up + depHash, link)
}
function top (name, hash) {
const link = path.join(nm, name)
fs.mkdirSync(path.dirname(link), { recursive: true })
fs.symlinkSync((name.includes('/') ? '../.pgh/' : '.pgh/') + hash, link)
}
pkg('Hkc', '@holepunchto/keet-core', '4.22.28')
pkg('Hbp1', 'blind-peering', '2.1.2')
pkg('Hbp2', 'blind-peering', '2.9.3')
pkg('Hhc', 'hypercore', '11.37.0')
pkg('Hcs', '@holepunchto/call-swarm', '1.2.0')
wire('Hkc', 'blind-peering', 'Hbp2') // keet-core wants ^2.5.0
wire('Hkc', 'hypercore', 'Hhc')
wire('Hkc', '@holepunchto/call-swarm', 'Hcs')
wire('Hbp2', 'hypercore', 'Hhc') // a .pgh-only package's own dep
top('blind-peering', 'Hbp1') // the app's own (older) pin
top('hypercore', 'Hhc')
top('@holepunchto/keet-core', 'Hkc')
top('@holepunchto/call-swarm', 'Hcs')
execFileSync('node', [path.join(__dirname, '..', 'flatten-node-modules.cjs'), nm], { stdio: 'pipe' })
const real = p => { try { return fs.realpathSync(path.join(nm, p)) } catch { return null } }
const version = p => { try { return JSON.parse(fs.readFileSync(path.join(nm, p, 'package.json'))).version } catch { return null } }
ok(!fs.lstatSync(path.join(nm, '@holepunchto/keet-core')).isSymbolicLink(), 'top-level keet-core is a real copy')
ok(version('@holepunchto/keet-core/node_modules/blind-peering') === '2.9.3',
"keet-core's blind-peering is its own 2.9.3 (got " + version('@holepunchto/keet-core/node_modules/blind-peering') + ')')
ok(real('@holepunchto/keet-core/node_modules/blind-peering') === path.join(pgh, 'Hbp2'),
"keet-core's blind-peering loads from .pgh (no top-level copy of that hash)")
ok(real('@holepunchto/keet-core/node_modules/hypercore') === real('hypercore'),
"keet-core's hypercore is the top-level copy (one instance)")
ok(real('@holepunchto/keet-core/node_modules/@holepunchto/call-swarm') === real('@holepunchto/call-swarm'),
"keet-core's scoped dep is the top-level copy (one instance)")
ok(real('.pgh/Hbp2/node_modules/hypercore') === real('hypercore'),
"a .pgh package's dep is the top-level copy (one instance)")
ok(version('blind-peering') === '2.1.2', "the app's own blind-peering is untouched")
const dangling = []
;(function walk (d) {
for (const e of fs.readdirSync(d, { withFileTypes: true })) {
const p = path.join(d, e.name)
if (e.isSymbolicLink()) { if (!fs.existsSync(p)) dangling.push(path.relative(nm, p)) } else if (e.isDirectory()) walk(p)
}
})(nm)
ok(dangling.length === 0, 'no dangling links (' + dangling.join(', ') + ')')
fs.rmSync(dir, { recursive: true, force: true })
console.log(failed === 0 ? 'PASS flatten-node-modules' : 'FAIL flatten-node-modules (' + failed + ')')
process.exit(failed === 0 ? 0 : 1)