mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 22:47:14 +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
45 lines
2.0 KiB
JavaScript
45 lines
2.0 KiB
JavaScript
// The JSON/MJS bases are data, but data a consumer's tool parses: a typo in
|
|
// one is a broken lint/knip/jscpd run in every repo at once.
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { strykerBase } from "../stryker.base.mjs";
|
|
|
|
const ROOT = path.join(import.meta.dirname, "..");
|
|
|
|
describe("bases", () => {
|
|
it("stryker never breaks the build on score", () => {
|
|
expect(strykerBase.thresholds.break).toBeNull();
|
|
expect(strykerBase.testRunner).toBe("vitest");
|
|
expect(strykerBase.mutate).toContain("!src/**/*.test.*");
|
|
});
|
|
|
|
it("tsconfig base carries the two flags the article names", () => {
|
|
const raw = fs.readFileSync(path.join(ROOT, "tsconfig.base.json"), "utf8");
|
|
// JSONC: strip the comment lines before parsing.
|
|
const json = JSON.parse(raw.split("\n").filter((line) => !line.trimStart().startsWith("//")).join("\n"));
|
|
expect(json.compilerOptions.strict).toBe(true);
|
|
expect(json.compilerOptions.noUncheckedIndexedAccess).toBe(true);
|
|
expect(json.compilerOptions.exactOptionalPropertyTypes).toBe(true);
|
|
expect(json.compilerOptions.outDir).toBeUndefined();
|
|
});
|
|
|
|
it("knip and jscpd bases are valid JSON with exit-1 semantics", () => {
|
|
const knip = JSON.parse(fs.readFileSync(path.join(ROOT, "knip.base.json"), "utf8"));
|
|
expect(Object.values(knip.rules).every((level) => level === "error")).toBe(true);
|
|
const jscpd = JSON.parse(fs.readFileSync(path.join(ROOT, "jscpd.base.json"), "utf8"));
|
|
expect(jscpd.exitCode).toBe(1);
|
|
expect(jscpd.threshold).toBe(0);
|
|
});
|
|
|
|
it("every export in package.json points at a shipped file", () => {
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8"));
|
|
for (const target of Object.values(manifest.exports)) {
|
|
expect(fs.existsSync(path.join(ROOT, target)), target).toBe(true);
|
|
const [, top] = target.split("/", 2);
|
|
expect(manifest.files, `${top} missing from "files"`).toContain(top);
|
|
}
|
|
});
|
|
});
|