testPagesAreDefinitions
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/); }