mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 20:07:18 +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
30 lines
1.2 KiB
JavaScript
30 lines
1.2 KiB
JavaScript
// One deliberate violation per rule family in test/fixture, and the rule id
|
|
// that must catch it. A rule missing from this table is a rule the preset
|
|
// does not prove; a fixture file missing from it is a plant nobody checks.
|
|
export const EXPECTED = {
|
|
"e2e/home.spec.ts": [
|
|
"playwright/missing-playwright-await",
|
|
"playwright/no-conditional-in-test",
|
|
],
|
|
"src/components/button.tsx": [
|
|
"jsx-a11y/alt-text",
|
|
"jsx-a11y/click-events-have-key-events",
|
|
"react-hooks/set-state-in-effect",
|
|
"react-you-might-not-need-an-effect/no-derived-state",
|
|
],
|
|
"src/design-system/theme.ts": ["boundaries/dependencies"],
|
|
"src/helper.test.ts": ["vitest/expect-expect", "vitest/no-focused-tests"],
|
|
"src/pages/home.tsx": ["boundaries/dependencies"],
|
|
"src/router/routes.ts": ["boundaries/dependencies"],
|
|
"src/services/api.ts": [
|
|
"@typescript-eslint/no-explicit-any",
|
|
"@typescript-eslint/no-non-null-assertion",
|
|
"boundaries/dependencies",
|
|
"no-restricted-imports",
|
|
],
|
|
"src/Wrong_Name.ts": ["unicorn/filename-case"],
|
|
};
|
|
|
|
/** Files that must lint clean: proves the presets do not fire on good code. */
|
|
export const CLEAN = ["src/mocks/user.ts", "src/utils/helper.ts"];
|