pickVersions
uses tcmts.loader.packages.candidates, which uses tcmts.loader.packages.registryView, which uses node:child_process
Pick versions for packages the lock doesn't have yet: the newest in each range whose peers accept what's locked. Peers among the packages being picked are picked first, so React comes before React DOM.
import { satisfies as satisfies_ } from "#codebase/tcmts/core/semver"; import { candidates } from "#codebase/tcmts/loader/packages"; import type { Candidate, PackageLock } from "#codebase/tcmts/loader/packages"; export function pickVersions(wanted: { name: string; range: string }[], lock: PackageLock): void { const remaining = new Map(wanted.map((w) => [`${w.name}@${w.range}`, w])); const lockedVersions = (name: string) => lock.all().filter((l) => l.name === name).map((l) => l.version); const pending = (name: string) => [...remaining.values()].some((w) => w.name === name); while (remaining.size) { const all = [...remaining.values()]; // something whose peers are all settled; with a cycle, any const next = all.find((w) => candidates(w.name, w.range).every((c) => Object.keys(c.peers).every((p) => p === w.name || !pending(p)))) ?? all[0]!; remaining.delete(`${next.name}@${next.range}`); const options = candidates(next.name, next.range); if (!options.length) throw new Error(`can't find a version of ${next.name} in range ${next.range} (is the registry reachable?)`); const fits = (c: Candidate) => Object.entries(c.peers).every(([peer, range]) => { const versions = lockedVersions(peer); return !versions.length || versions.some((v) => satisfies_(v, range)); }); lock.set(next.name, next.range, (options.find(fits) ?? options[0]!).version); } }
uses
- tcmts.core.semver.satisfies
- tcmts.loader.packages.Candidate
- tcmts.loader.packages.PackageLock
- tcmts.loader.packages.candidates