import { readdirSync, readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import js from "@eslint/js"; import { ESLint } from "eslint"; import globals from "globals"; import prettier from "prettier"; import { describe, expect, it } from "vitest"; // The repo-root `scripts/*.mjs` (the lock check `siftlode check` runs, the CHANGELOG generator its // release runs) sit outside every lint lane: the eslint and prettier lanes run from frontend/, and an // ESLint config cannot reach past its own directory. This holds them to the same bar as // the frontend's plain-JS config files — `js.configs.recommended` under Node, and the frontend's // Prettier style. Not the typed rules: they are not in a tsc project (docs/gates.md). const REPO = fileURLToPath(new URL("../../../", import.meta.url)); const FRONTEND = `${REPO}frontend/`; const SCRIPTS = readdirSync(`${REPO}scripts`) .filter((f) => f.endsWith(".mjs")) .map((f) => `scripts/${f}`); describe("repo-root scripts", () => { it("are found", () => { expect(SCRIPTS).toContain("scripts/check-node-modules.mjs"); }); it("pass the plain-JS lint", async () => { const eslint = new ESLint({ cwd: REPO, overrideConfigFile: true, overrideConfig: [ { files: ["scripts/*.mjs"], ...js.configs.recommended, languageOptions: { globals: globals.node }, linterOptions: { reportUnusedDisableDirectives: "error" }, }, ], }); const hits = (await eslint.lintFiles(SCRIPTS)).flatMap((r) => r.messages.map((m) => `${r.filePath}:${m.line} ${m.ruleId ?? "parse"}: ${m.message}`), ); expect(hits).toEqual([]); }); it("are formatted in the frontend's Prettier style", async () => { const options = await prettier.resolveConfig(`${FRONTEND}eslint.config.js`); const unformatted: string[] = []; for (const f of SCRIPTS) { const ok = await prettier.check(readFileSync(REPO + f, "utf8"), { ...options, filepath: REPO + f, }); if (!ok) unformatted.push(f); } expect(unformatted).toEqual([]); }); });