70 lines
2.2 KiB
JavaScript
70 lines
2.2 KiB
JavaScript
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([]);
|
|
});
|
|
});
|