import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; const ROOT_DIR = path.resolve(import.meta.dirname, ".."); const SCAN_ROOTS = ["README.md", "docs", ".gitea/workflows"]; const COMMAND_RE = /\bpnpm\s+run\s+([A-Za-z0-9:._-]+)/g; const TRAILING_PUNCTUATION_RE = /[.,;:!?]+$/; function readRootScripts(rootDir = ROOT_DIR) { const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); return new Set(Object.keys(packageJson.scripts ?? {})); } function walkFiles(rootDir, relativePath, files) { const absolutePath = path.join(rootDir, relativePath); if (!fs.existsSync(absolutePath)) return; const stat = fs.statSync(absolutePath); if (stat.isFile()) { if (/\.(md|ya?ml)$/.test(relativePath)) files.push(relativePath); return; } for (const entry of fs.readdirSync(absolutePath, { withFileTypes: true })) { walkFiles(rootDir, path.join(relativePath, entry.name), files); } } function lineAndColumnForOffset(text, offset) { const before = text.slice(0, offset); const lines = before.split("\n"); return { line: lines.length, column: lines.at(-1).length + 1 }; } function normalizeScriptName(scriptName) { return scriptName.replace(TRAILING_PUNCTUATION_RE, ""); } function findMissingCommandReferences(rootDir = ROOT_DIR, scanRoots = SCAN_ROOTS) { const rootScripts = readRootScripts(rootDir); const files = []; for (const scanRoot of scanRoots) { walkFiles(rootDir, scanRoot, files); } return files.flatMap((file) => { const absolutePath = path.join(rootDir, file); const text = fs.readFileSync(absolutePath, "utf8"); const misses = []; for (const match of text.matchAll(COMMAND_RE)) { const scriptName = normalizeScriptName(match[1]); if (rootScripts.has(scriptName)) continue; const { line, column } = lineAndColumnForOffset(text, match.index ?? 0); misses.push(`${file}:${line}:${column}: unknown package script "${scriptName}"`); } return misses; }); } describe("documented pnpm run commands", () => { it("reference package.json scripts", () => { expect(findMissingCommandReferences()).toEqual([]); }); });