mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 22:07:15 +01:00
Evil Martians' "ten anti-AI-slop moves" as a package: strict tsconfig base (noUncheckedIndexedAccess, exactOptionalPropertyTypes), type-checked typescript-eslint strict+stylistic, unicorn/sonarjs/perfectionist, behaviour rules for tests (expect-expect, no-focused-tests), React (hooks, @eslint-react, jsx-a11y strict, you-might-not-need-an-effect), Playwright, and layer boundaries via eslint-plugin-boundaries 7 with the article's default policies. Knip / jscpd / Stryker bases; Stryker carries no break threshold on purpose. Plugins are dependencies, not devDependencies: a flat config imports them from this package's own node_modules, so a consumer installing from the git tag gets them or the lint cannot run. No build step, so the tag needs no prepare. test/fixture is a mini repo with one planted violation per rule family; test/expected.js names the rule that must catch each, and the suite runs the real CLI over it (in a child process -- v8 coverage merges natively-loaded and transformed copies of the same module into "uncovered"). 35 tests, 100% branch coverage on the factories. eslint/fast.js is the type-free subset for a per-edit hook: 0.52 s cold on one file, measured against 1.28 s for base() without types -- importing a plugin is the cost, so it loads none, nor base.js which imports them. Probed under ESLint 10: jsx-a11y 6.10.2 (peer says <=9) and boundaries 7.2.0 both run; the fixture pins that. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnAYyNH1Sm4iJNidfYbrZJ
65 lines
2.2 KiB
JavaScript
65 lines
2.2 KiB
JavaScript
// Runs the real preset over the fixture mini-repo and checks that every
|
|
// planted violation is caught by the rule the table names, and that the
|
|
// clean files stay clean.
|
|
//
|
|
// The lint runs as the CLI in a child process, exactly as a consumer's
|
|
// pre-commit hook runs it. It also keeps the preset modules out of this
|
|
// process: v8 coverage merges the natively-loaded copies ESLint imports with
|
|
// the vitest-transformed ones the unit tests import, and the merge reports
|
|
// executed lines as uncovered.
|
|
import { execFileSync } from "node:child_process";
|
|
import path from "node:path";
|
|
import { beforeAll, describe, expect, it } from "vitest";
|
|
|
|
import { CLEAN, EXPECTED } from "./expected.js";
|
|
|
|
const FIXTURE = path.join(import.meta.dirname, "fixture");
|
|
const ESLINT_BIN = path.join(import.meta.dirname, "..", "node_modules", "eslint", "bin", "eslint.js");
|
|
|
|
/** @type {Map<string, Set<string>>} relative path -> rule ids reported */
|
|
const found = new Map();
|
|
|
|
beforeAll(() => {
|
|
let stdout;
|
|
try {
|
|
stdout = execFileSync(process.execPath, [ESLINT_BIN, ".", "--format", "json"], {
|
|
cwd: FIXTURE,
|
|
encoding: "utf8",
|
|
});
|
|
} catch (error) {
|
|
// Planted violations make ESLint exit 1; the JSON report is still on stdout.
|
|
stdout = error.stdout;
|
|
}
|
|
const results = JSON.parse(stdout.slice(stdout.indexOf("[")));
|
|
for (const result of results) {
|
|
const relative = path.relative(FIXTURE, result.filePath);
|
|
found.set(relative, new Set(result.messages.map((m) => m.ruleId)));
|
|
}
|
|
}, 120_000);
|
|
|
|
describe("planted violations", () => {
|
|
for (const [file, rules] of Object.entries(EXPECTED)) {
|
|
for (const rule of rules) {
|
|
it(`${file}: ${rule}`, () => {
|
|
expect([...(found.get(file) ?? [])]).toContain(rule);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
describe("clean files", () => {
|
|
for (const file of CLEAN) {
|
|
it(`${file} reports nothing`, () => {
|
|
expect([...(found.get(file) ?? [])]).toEqual([]);
|
|
});
|
|
}
|
|
});
|
|
|
|
it("every fixture file is accounted for", () => {
|
|
const expectedFiles = new Set(Object.keys(EXPECTED));
|
|
const known = expectedFiles.union(new Set(CLEAN));
|
|
for (const file of found.keys()) {
|
|
expect(known.has(file), `unlisted fixture file: ${file}`).toBe(true);
|
|
}
|
|
});
|