testTcmLibrariesHoldWhatTheyUse

tcmts.cli.test.e2e.libraries.testTcmLibrariesHoldWhatTheyUse · #tjxtyi3w · term

A tcm library (npm: false): released only if its namespace holds everything its code uses, so a codebase that adds it has every definition named; a package of its codebase that uses it carries its code rather than depending on it from npm; and it isn't packed itself.

import { codebase } from "#codebase/tcmts/cli/test/e2e/releases";
import assert from "node:assert/strict";
import fs from "node:fs";
import path_1 from "node:path";

export const testTcmLibrariesHoldWhatTheyUse = () => {
  const alice = codebase("tcm library");
  alice.save(`// tcm: namespace util\nexport const square = (n: number) => n * n;\n`, "add");
  alice.save(
    `// tcm: namespace geo\nimport { square } from "#codebase/util";\nexport const manifest = { name: "geo", npm: false };\n/** A square's area. */\nexport const area = (side: number) => square(side);\n`,
    "add",
  );
  // geo uses util.square, which isn't its own: a codebase adding it would have that unnamed
  const refused = alice.run("release", "geo");
  assert.notEqual(refused.status, 0);
  assert.match(refused.out, /geo is a tcm library, and it uses what isn't its own: util\.square/);
  const checked = alice.run("release", "check", "geo");
  assert.notEqual(checked.status, 0);
  assert.match(
    checked.out,
    /geo's first release: 0\.1\.0[\s\S]*not released as it stands: geo is a tcm library/,
  );

  // moved in, it's released (with a README: a file kept with it, which isn't its API)
  alice.tcm("mv", "util.square", "geo.square");
  fs.writeFileSync(alice.file("README.md"), "<!-- tcm: path geo.README -->\n# geo\n\nShapes.\n");
  alice.tcm("add", "README.md");
  assert.match(alice.tcm("release", "geo"), /released geo@0\.1\.0/);
  alice.tcm("patch", "geo.square", "--old", "n * n", "--new", "n ** 2");
  const next = alice.tcm("release", "check", "geo");
  assert.match(next, /geo's next release: 0\.1\.1/);
  assert.doesNotMatch(next, /README/);
  // not packed for npm, and another package of the codebase carries it rather than depending on it
  assert.match(alice.run("pack", "geo").out, /geo is a tcm library \(npm: false\)/);
  alice.save(
    `// tcm: namespace shapes\nimport { area } from "#codebase/geo";\nexport const manifest = { name: "shapes" };\nexport const tile = () => area(3);\n`,
    "add",
  );
  alice.tcm("pack", "shapes");
  const packed = path_1.join(alice.dir, ".tcm", "pack", "shapes");
  const pkg = JSON.parse(fs.readFileSync(path_1.join(packed, "package.json"), "utf8")) as {
    dependencies?: Record<string, string>;
  };
  assert.equal(pkg.dependencies?.geo, undefined, JSON.stringify(pkg));

  // a fresh codebase adds it: every definition it brings is named there
  const me = codebase("adds a tcm library");
  me.save(`// tcm: namespace app\nexport const greeting = "hi";\n`, "add");
  assert.match(me.tcm("lib", "add", alice.dir, "geo@0.1.0"), /added app\.lib\.geo: geo@0\.1\.0/);
  const deps = me.tcm("deps", "app.lib.geo.area");
  assert.match(deps, /app\.lib\.geo\.square/);
  assert.doesNotMatch(deps, /^#/m, deps);
  me.save(
    `// tcm: namespace app\nimport { area } from "#codebase/app/lib/geo";\nexport const total = () => area(4);\n`,
    "add",
  );
  assert.equal(me.tcm("run", "app.total").trim(), "16");
}

uses