checkLibraries

tcmts.dev.checkLibraries · #bsffijo4 · term · runs on the server

uses node:fs

Each of tcm's libraries (TCM_LIBRARIES), added to a fresh codebase as another codebase would add it, by its namespace as the branch this runs on has it (released or not: a library's releases are made on main once it's landed): every definition it brings must be named there (nothing it uses arrives as an unnamed version). pnpm verify runs it; pnpm tcm run tcmts.dev.checkLibraries does too.

import { Codebase } from "#codebase/tcmts/cli/codebase";
import { addLibrary, libraryName, releaseNamespace, within } from "#codebase/tcmts/cli/libraries";
import { packagesIn } from "#codebase/tcmts/cli/manifests";
import { codebaseDir, TCM_LIBRARIES } from "#codebase/tcmts/dev";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";

export async function checkLibraries(): Promise<void> {
  const session = process.env.TCM_BRANCH;
  // (each library's namespace here, on this branch)
  const here = Codebase.open(codebaseDir());
  const head = here.store.head();
  const packages = await Promise.all(
    packagesIn(here, here.store.names()).map(async (p) => ({
      name: p.name,
      namespace: await releaseNamespace(here.store, {
        package: p.name,
        version: "",
        namespace: p.namespace,
        causal: head,
      }),
    })),
  );
  here.store.close();
  delete process.env.TCM_BRANCH;
  try {
    for (const name of TCM_LIBRARIES) {
      const dir = fs.mkdtempSync(path.join(os.tmpdir(), `tcm library ${name} `));
      try {
        const cb = Codebase.init(dir);
        const pkg = packages.find((p) => p.name === name);
        if (!pkg) throw new Error(`${name}: no package of that name on ${session ?? "main"}`);
        // (by hash, which names no package: called after it, as `tcm lib add` calls a release)
        const lib = await addLibrary(cb, {
          name: libraryName(name),
          source: codebaseDir(),
          what: `#${pkg.namespace}`,
        });
        const rows = cb.store.names();
        const named = new Set(rows.map((r) => r.hash));
        const unnamed = new Set<string>();
        for (const r of rows.filter((x) => within(x.path, lib.path))) {
          const rec = cb.store.getDef(r.hash);
          if (!rec) continue;
          for (const m of cb.store.componentMembers(rec.componentHash))
            for (const d of m.deps)
              if (d.ref.startsWith("#") && !named.has(d.ref.slice(1)))
                unnamed.add(`${r.path} uses #${d.ref.slice(1, 9)}`);
        }
        if (unnamed.size)
          throw new Error(
            `${name}, added to a fresh codebase, uses what it doesn't name:\n  ${[...unnamed].join("\n  ")}`,
          );
        console.log(
          `${name}: added as ${lib.path} (as ${session ?? "main"} has it, #${pkg.namespace.slice(0, 8)}), every definition named`,
        );
      } finally {
        fs.rmSync(dir, { recursive: true, force: true });
      }
    }
  } finally {
    if (session !== undefined) process.env.TCM_BRANCH = session;
  }
}

uses