testImportGlobalAugmentation

tcmts.cli.test.e2e.import.testImportGlobalAugmentation · #sxzekjeg · term

A declare global augmentation is a definition of its own (<file>.global) that every check in the codebase includes: code relying on it typechecks whether or not it imports the augmenting file, scratch files see it, and editing it checks the new version alone.

import { okIn, 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 testImportGlobalAugmentation = () => {
  const dir = tempDir("augment");
  const lib = tempDir("augment lib");
  const tcm = (...args: string[]) => okIn(dir, ...args);
  tcm("init");
  const write = (file: string, text: string) => {
    fs.mkdirSync(path.dirname(path.join(lib, file)), { recursive: true });
    fs.writeFileSync(path.join(lib, file), text);
  };
  write(
    "tsconfig.json",
    JSON.stringify({ compilerOptions: { strict: true, lib: ["ESNext"], target: "ES2022" } }),
  );
  write(
    "src/errors.ts",
    `export const exitCode = "~lib/exitCode" as const;

/** What errors may carry. */
declare global {
  interface Error {
    readonly [exitCode]?: number;
    readonly hint?: string;
  }
}

export const codeOf = (e: Error): number => e[exitCode] ?? 1;
`,
  );
  // relies on the augmentation without importing the file it's in
  write("src/hints.ts", `export const hintOf = (e: Error): string => e.hint ?? "none";\n`);
  assert.match(tcm("import", path.join(lib, "src"), "--into", "lib"), /\+ new 4/);
  assert.match(tcm("view", "lib.errors.global"), /declare global \{\n\s+interface Error/);
  assert.match(tcm("verify"), /verified \d+ definitions/);

  fs.writeFileSync(
    path.join(dir, "scratch.ts"),
    `// tcm: namespace app
import { codeOf } from "#codebase/lib/errors";
import { hintOf } from "#codebase/lib/hints";

export function main() {
  const e = new Error("x");
  console.log("errors", codeOf(e), hintOf(e), e.hint === undefined);
}
`,
  );
  tcm("add");
  assert.match(tcm("run", "app.main"), /errors 1 none true/);

  // a new version of the augmentation is checked without the stored one, which declares the same members
  tcm("edit", "lib.errors.global");
  const scratch = path.join(dir, "scratch.ts");
  fs.writeFileSync(
    scratch,
    fs
      .readFileSync(scratch, "utf8")
      .replace(
        "readonly hint?: string;",
        "readonly hint?: string;\n    readonly retries?: number;",
      ),
  );
  tcm("update");
  fs.writeFileSync(
    scratch,
    `// tcm: namespace app\nexport const retriesOf = (e: Error): number => e.retries ?? 0;\n`,
  );
  tcm("add");
  assert.match(tcm("run", "app.retriesOf", "--", "{}"), /0/);
}

uses