testMirrorNewNamespacesAndNearbyRenames

tcmts.cli.test.e2e.mirror.testMirrorNewNamespacesAndNearbyRenames · #lbnw6ad4 · term

a mirror makes files for new namespaces, and renames reach files near it

import { okIn, tempDir } from "#codebase/tcmts/cli/test/e2e/harness";
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";

export const testMirrorNewNamespacesAndNearbyRenames = () => {
  const root = tempDir("mirror");
  const repo = path.join(root, "near");
  const cbDir = path.join(root, "near-cb");
  fs.mkdirSync(path.join(repo, "src"), { recursive: true });
  fs.mkdirSync(path.join(repo, "scripts"));
  fs.writeFileSync(path.join(repo, "package.json"), JSON.stringify({ type: "module" }));
  fs.writeFileSync(path.join(repo, "src", "math.ts"), `export function double(x: number): number {\n  return x * 2;\n}\n`);
  // outside the mirror (src/), in the same repository
  const report = `import { double } from "../src/math.ts";\n\nconsole.log(double(21));\n`;
  fs.writeFileSync(path.join(repo, "scripts", "report.ts"), report);
  assert.equal(spawnSync("git", ["-C", repo, "init", "-q"]).status, 0);
  fs.mkdirSync(cbDir);
  const run = (...args: string[]) => okIn(cbDir, ...args);
  run("init");
  run("import", path.join(repo, "src"), "--into", "app", "--mirror");

  // a namespace with no file yet gets one, where the mirror reads it back as that namespace
  fs.writeFileSync(path.join(cbDir, "scratch.ts"), `// tcm: namespace app.extra\nimport { double } from "#codebase/app/math";\nexport const quad = (x: number): number => double(double(x));\n`);
  assert.match(run("add"), /mirror: wrote extra\.ts/);
  assert.equal(fs.readFileSync(path.join(repo, "src", "extra.ts"), "utf8"), `import { double } from "./math.ts";\n\nexport const quad = (x: number): number => double(double(x));\n`);

  // a rename reaches the script that uses it, outside the mirror
  assert.match(run("mv", "app.math.double", "app.math.twice"), /mv|moved/);
  assert.equal(fs.readFileSync(path.join(repo, "scripts", "report.ts"), "utf8"), report.replaceAll("double", "twice"));
  assert.match(fs.readFileSync(path.join(repo, "src", "extra.ts"), "utf8"), /twice\(twice\(x\)\)/);
  assert.match(run("mirror", "--check"), /the files hash to what the codebase holds/);

  // code edited in a scratch file that spells an import its own way is kept as the file spells it, so the next
  // write doesn't bring the scratch file's spelling in
  fs.writeFileSync(path.join(repo, "src", "paths.ts"), `import path from "node:path";\n\nexport const base = (p: string): string => path.basename(p);\n`);
  run("import", path.join(repo, "src"), "--into", "app", "--update");
  fs.writeFileSync(path.join(cbDir, "scratch.ts"), `// tcm: namespace app.paths\nimport nodePath from "node:path";\nexport const base = (p: string): string => nodePath.basename(p).toUpperCase();\n`);
  run("update");
  assert.match(fs.readFileSync(path.join(repo, "src", "paths.ts"), "utf8"), / path\.basename\(p\)\.toUpperCase\(\)/);
  assert.match(run("view", "app.paths.base"), / path\.basename\(p\)\.toUpperCase\(\)/);

  // in the codebase anything can be imported; in the files, what another file imports is exported where it is
  fs.appendFileSync(path.join(repo, "src", "paths.ts"), `\nconst SEP = "/";\n\nexport const join = (a: string, b: string): string => a + SEP + b;\n`);
  fs.writeFileSync(path.join(cbDir, "scratch.ts"), `// tcm: namespace app.extra\nimport { SEP } from "#codebase/app/paths";\nexport const under = (p: string): string => SEP + p;\n`);
  run("add");
  assert.match(fs.readFileSync(path.join(repo, "src", "paths.ts"), "utf8"), /\nexport const SEP = "\/";\n/);
  assert.match(fs.readFileSync(path.join(repo, "src", "extra.ts"), "utf8"), /import \{ SEP \} from "\.\/paths\.ts";/);
  assert.match(run("mirror", "--check"), /the files hash to what the codebase holds/);

  // saved from a scratch file and then re-derived in the same update (it uses one that was): its new code is
  // written, not left as propagation-only
  fs.writeFileSync(path.join(repo, "src", "chain.ts"), `export const h = (): number => 1;\nexport const g = (): number => h() + 1;\nexport const f = (): number => g() * 10;\n`);
  fs.writeFileSync(path.join(cbDir, "scratch.ts"), `// tcm: namespace app.chain\nimport { g } from "#codebase/app/chain";\nexport const h = (): number => 2;\nexport const f = (): number => g() * 100;\n`);
  assert.match(run("update"), /mirror: wrote chain\.ts/);
  assert.equal(fs.readFileSync(path.join(repo, "src", "chain.ts"), "utf8"), `export const h = (): number => 2;\nexport const g = (): number => h() + 1;\nexport const f = (): number => g() * 100;\n`);
  assert.match(run("mirror", "--check"), /the files hash to what the codebase holds/);
}

uses