pickVersions

tcmts.loader.packages.pickVersions · #nasv44a2 · term · runs on the server

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

used by