provide

tcmts.cli.pack.provide · #oeb7o6ay · term · runs anywhere

Which components the package carries, and what it takes from its sibling packages instead: a definition one of them exports comes from the oldest release of it that exports every such definition used, as it is here. One a sibling keeps to itself (it doesn't export it) is carried, like the package's own code: two packages of one codebase can share code without either publishing it.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import type { Sibling } from "#codebase/tcmts/cli/pack";

export function provide(cb: Codebase, roots: string[], siblings: Sibling[]) {
  const ownerOf = new Map<string, Sibling>();
  for (const s of siblings) for (const h of s.owned.keys()) ownerOf.set(h, s);
  const rootSet = new Set(roots);
  const used = new Map<Sibling, Set<string>>();
  const kept = new Set<string>();
  const queue = [...roots];
  while (queue.length) {
    const h = queue.pop()!;
    const owner = ownerOf.get(h);
    if (owner && !rootSet.has(h) && owner.exports.has(h)) {
      used.set(owner, (used.get(owner) ?? new Set()).add(h));
      continue;
    }
    const rec = cb.store.getDef(h);
    if (!rec || kept.has(rec.componentHash)) continue;
    kept.add(rec.componentHash);
    for (const m of cb.store.componentMembers(rec.componentHash))
      for (const d of m.deps) if (d.ref.startsWith("#")) queue.push(d.ref.slice(1));
  }
  const provided = new Map<string, { module: string; name: string }>();
  const links: Record<string, string> = {};
  for (const [s, hashes] of used) {
    // the oldest release with all of it: the widest range of the other's versions this works with
    const release = s.releases.findLast((r) => [...hashes].every((h) => r.exported.has(h)));
    if (!release) {
      const latest = s.releases[0];
      const missing = [...hashes]
        .filter((h) => !latest?.exported.has(h))
        .map((h) => s.owned.get(h)!)
        .sort();
      throw new Error(
        `this uses ${missing.join(", ")} from ${s.name}, which ${latest ? `${s.name}@${latest.version} doesn't export as it is here` : `${s.name} hasn't released yet`}: release ${s.name} first (\`tcm release ${s.name}\`)`,
      );
    }
    links[s.name] = `^${release.version}`;
    for (const h of hashes) {
      const e = release.exported.get(h)!;
      provided.set(h, {
        module: e.subpath === "." ? s.name : `${s.name}/${e.subpath.replace(/^\.\//, "")}`,
        name: e.name,
      });
    }
  }
  return { kept, provided, links };
}

uses

used by