testPagesAreDefinitions

tcmts.cli.test.e2e.files.testPagesAreDefinitions · #boh3trqb · term

Pages are definitions: links to code follow renames, links to pages follow moves, and they're edited as files.

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

export const testPagesAreDefinitions = () => {
  const dir = tempDir("files");
  const ok = (...args: string[]) => okIn(dir, ...args);
  const write = (file: string, text: string) => {
    fs.mkdirSync(path.dirname(path.join(dir, file)), { recursive: true });
    fs.writeFileSync(path.join(dir, file), text);
  };
  ok("init");
  write("scratch.ts", `// tcm: namespace lib\nexport const square = (x: number) => x * x;\n`);
  ok("add");
  write(
    "pages/guide.md",
    "<!-- tcm: namespace docs -->\n# Guide\n\nUse {@link lib.square}. See [the FAQ](faq.md#why), not `[this](code.md)`.\n\n```md\n[nor this](fence.md)\n```\n",
  );
  write(
    "pages/faq.md",
    "<!-- tcm: namespace docs -->\n# FAQ\n\n## Why\n\nBack to [the guide](guide.md).\n",
  );
  // a link to a page that isn't there yet is a warning; a {@link} to no code is a problem
  assert.match(
    ok("check", "pages/guide.md"),
    /warning: docs\.guide links to faq\.md#why, and there's no docs\.faq[\s\S]*\+ new +docs\.guide : md[\s\S]*tcm add pages\/guide\.md/,
  );
  write("pages/bad.md", "<!-- tcm: namespace docs -->\nSee {@link lib.nothing}.\n");
  assert.match(
    tcmIn(dir, "add", "pages/bad.md").out,
    /\{@link lib\.nothing\}: there's no lib\.nothing in the codebase/,
  );
  ok("add", "pages/faq.md");
  ok("add", "pages/guide.md");
  assert.match(ok("ls", "docs"), /docs\.faq +#\w+ +: md\ndocs\.guide +#\w+ +: md/);

  // renaming the code changes no page's text: the page names it as it's called now
  ok("mv", "lib.square", "lib.sq");
  assert.match(ok("view", "docs.guide"), /Use \{@link lib\.sq\}\./);
  // moving a page follows it in the pages that link to it (and code using those follows them), and refuses to leave
  // a link with nowhere to go
  write(
    "scratch.ts",
    `// tcm: namespace site\nimport { guide } from "#codebase/docs";\nexport const pages = { guide };\n`,
  );
  ok("add");
  assert.match(ok("mv", "docs.faq", "docs.questions"), /links to it changed in docs\.guide/);
  assert.match(ok("view", "docs.guide"), /\[the FAQ\]\(questions\.md#why\)/);
  assert.match(ok("todo"), /nothing to do/);
  assert.match(tcmIn(dir, "rm", "docs.questions").out, /'docs\.questions' is used by docs\.guide/);

  // edited as the file it is, and saved back to where it came from
  ok("edit", "docs.guide");
  const edited = path.join(dir, "guide.md");
  assert.match(fs.readFileSync(edited, "utf8"), /^<!-- tcm: path docs\.guide -->\n# Guide/);
  fs.writeFileSync(edited, fs.readFileSync(edited, "utf8").replace("# Guide", "# The guide"));
  assert.match(ok("update", "guide.md"), /~ update +docs\.guide : md/);

  // written out as files, links as they are now; and re-hashed by verify
  ok("export", "docs", "site");
  assert.match(
    fs.readFileSync(path.join(dir, "site", "guide.md"), "utf8"),
    /^# The guide\n\nUse \{@link lib\.sq\}\. See \[the FAQ\]\(questions\.md#why\)/,
  );
  assert.ok(fs.existsSync(path.join(dir, "site", "questions.md")));
  assert.match(ok("verify"), /verified 4 definitions/);
}

uses