linkPeers
uses node:fs
Point each installed package's peers at the locked range its peer range accepts. Package managers resolve a peer by name across the whole install, so with two ranges of React in the cache, React DOM 18 could get React 19 (pnpm), or a second copy at the same version (npm). Two copies of one React break it, and the wrong one breaks it worse. A link inside the package wins: Node resolution looks there first.
import { compareVersions, satisfies as satisfies_ } from "#codebase/tcmts/core/semver"; import type { Layout } from "#codebase/tcmts/loader"; import { installDir, oneCopyEach, resolvesTo } from "#codebase/tcmts/loader/packages"; import type { PackageLock } from "#codebase/tcmts/loader/packages"; import fs from "node:fs"; import path from "node:path"; export function linkPeers(lay: Layout, lock: PackageLock) { const locked = lock.all(); // (and the copy npm puts at a peer's name is the alias's) oneCopyEach(lay, locked); for (const l of locked) { const dir = installDir(lay, l.name, l.range); let real: string; let peers: Record<string, string>; try { real = fs.realpathSync(dir); peers = ( JSON.parse(fs.readFileSync(path.join(real, "package.json"), "utf8")) as { peerDependencies?: Record<string, string>; } ).peerDependencies ?? {}; } catch { continue; } for (const [peer, range] of Object.entries(peers)) { const fits = locked .filter((p) => p.name === peer && satisfies_(p.version, range)) .sort((a, b) => compareVersions(b.version, a.version))[0]; if (!fits) continue; let target: string; try { target = fs.realpathSync(installDir(lay, fits.name, fits.range)); } catch { continue; } if (resolvesTo(peer, real) === target) continue; const link = path.join(real, "node_modules", peer); fs.mkdirSync(path.dirname(link), { recursive: true }); fs.rmSync(link, { recursive: true, force: true }); fs.symlinkSync(target, link, "dir"); } } }
uses
- tcmts.core.semver.compareVersions
- tcmts.core.semver.satisfies
- tcmts.loader.Layout
- tcmts.loader.packages.PackageLock
- tcmts.loader.packages.installDir
- tcmts.loader.packages.oneCopyEach
- tcmts.loader.packages.resolvesTo