pagez

The rules, printed

pagez rules <id> prints one rule from the docs the package ships, so an agent reads the rule for the version it runs. An error or a warning prints when it fails or warns, and then its rule. A Markz code prints Markz's text for it. With no id, every rule prints, one a line.

import { readFileSync } from "node:fs";
import { WARNINGS, type WarningCode } from "@amitkaps/markz";

const DOCS = ["pages.md", "elements.md"];

/** The lines of a doc outside its code blocks. */
function lines(doc: string): string[] {
  let fenced = false;
  return readFileSync(new URL(`../docs/${doc}`, import.meta.url), "utf8")
    .split("\n")
    .filter((line) => {
      if (/^(```|~~~)/.test(line)) fenced = !fenced;
      return !fenced;
    });
}

/** A doc's Markz as plain text: an id's span as the id, and a link within the docs as its text. */
const plain = (text: string) =>
  text.replace(/\[`([^`]+)`\]\{#[^}]+\}/g, "$1").replace(/\[([^\]]+)\]\(([a-z.]*)#[^)]*\)/g, "$1");

/** Every rule, as `id: text`. */
function rules(): Map<string, string> {
  const found = new Map<string, string>();
  for (const doc of DOCS) {
    for (const line of lines(doc)) {
      const rule = /^- \[`([a-z][a-z0-9-]*)`\]\{#\1\}: (.+)$/.exec(line);
      if (rule) found.set(rule[1]!, plain(rule[2]!));
    }
  }
  return found;
}

/** Each error and warning, by its id, with when it fails or warns, and its rule's id. */
function rows(): Map<string, { when: string; rule: string }> {
  const found = new Map<string, { when: string; rule: string }>();
  for (const line of lines("pages.md")) {
    const row = /^\| \[`([a-z-]+)`\]\{#\1\} +\| (.+?) +\| +\[`([a-z0-9-]+)`\]/.exec(line);
    if (row) found.set(row[1]!, { when: plain(row[2]!), rule: row[3]! });
  }
  return found;
}

/** What `pagez rules` prints for `id`, or `undefined` when it names nothing. */
export function printedRule(id?: string): string | undefined {
  const all = rules();
  if (id === undefined) return [...all].map(([id, text]) => `${id}: ${text}`).join("\n");
  const rule = all.get(id);
  if (rule) return `${id}: ${rule}`;
  const row = rows().get(id);
  if (row) {
    const verb = id.startsWith("error-") ? "Fails" : "Warns";
    return `${id}: ${verb} when ${row.when}.\n\n${row.rule}: ${all.get(row.rule)}`;
  }
  if (!Object.hasOwn(WARNINGS, id)) return undefined;
  const markz = WARNINGS[id as WarningCode];
  return `${id}: Markz warns at ${markz.message}. Instead: ${markz.instead}.`;
}