Thirty-five eslint-disable comments, and no ESLint anywhere in the repository
I went looking for the lint config in CogniPrep and there isn't one. No .eslintrc, no eslint.config.mjs, no lint script among the 25 entries in package.json, and no eslint, @typescript-eslint, eslint-plugin-react-hooks o
I went looking for the lint config in CogniPrep and there isn't one. No .eslintrc, no eslint.config.mjs, no lint script among the 25 entries in package.json, and no eslint, @typescript-eslint, eslint-plugin-react-hooks or @next/eslint-plugin-next in either dependency block.
The lockfile mentions eslint nine times. Every one of them is @types/eslint, @types/eslint-scope or [email protected], which is the scope analyser webpack uses and its type definitions. The linter itself has never been installed.
There are still 35 eslint-disable comments in the source, spread over 27 files.
What the 35 comments ask for
30 react-hooks/exhaustive-deps (23 next-line, 7 line)
2 no-control-regex
1 no-var
1 @next/next/no-sync-scripts
1 @typescript-eslint/no-explicit-any
Four plugin namespaces are named between them and none of the four packages exists in the tree. The comments are documentation. They are addressed to a reader, not to a tool, and nothing in pnpm verify will ever parse them.
That makes them more interesting than they would be in a repo that lints, because nobody added them to silence a red squiggle. Somebody wrote each one to mark a place where the obvious rule does not apply.
The ones that are not about dependency arrays
no-var sits on a declare global block holding a Postgres client across hot reloads. Augmenting globalThis with let or const does not work, so var is not a style slip there, it is the only declaration that compiles. The rule is right in general and wrong on that line, which is exactly what the comment says.
@next/next/no-sync-scripts sits above the inline theme script in the root layout. The rule it names is about a <script src="..."> with no async or defer, and this script has no src at all, so it would not fire even with the plugin installed. The comment is inert twice over. It is still the clearest note in the file about what that script is: a blocking read of a stored theme, placed before first paint on purpose.
no-control-regex, the only rule disabled in two separate files, is the find I did not expect. Both files strip control characters out of user text. They do not agree on what a control character is.
// lib/pricing/price-feedback.ts
/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g
// lib/onboarding/constants.ts
/[\u0000-\u001F\u007F-\u009F]/g
The first keeps tab, line feed and carriage return, because it is cleaning a free-text comment where a newline is legitimate. The second strips all of C0 including those three, and goes on to strip the C1 range as well. Neither is wrong. They are two different jobs that read as one shared helper, and the thing stopping them from becoming one shared helper is that the onboarding copy has to stay importable from a client component while the other module does not.
So the same silenced rule marks the boundary between two bundles. I would not have found that by grepping for replace(.
The dependency arrays
Of the 30 exhaustive-deps comments, 21 are inside game components and 9 are not: seven in a single interview session component, one in an admin tool, one in the field that resolves a code at checkout. Seven in one file is a signal rather than a count. That component owns a recorder, a timer, a question index and a voice, and every one of those [] arrays is a deliberate "run once, ignore what changed", which is the single most common legitimate exception to the rule and also the single easiest way to ship a stale closure.
If I ever install ESLint, those nine are the ones I would read first. The 21 in the game components are mostly mount-once engines that the tests already pin.
What actually enforces anything here
Two things, and neither is a linter:
-
tsc --noEmiton TypeScript 6.0.3 - 375 Vitest files, 8,001 tests, 21 seconds
That is the real answer to why the disable comments never mattered. There is no CI either: the one GitHub Actions workflow in the repo takes a database backup and runs no checks. A lint rule catches a class of mistake that a type error or a failing assertion would also catch in this codebase, and both of those run locally through pnpm verify. The gap a linter would actually close is the dependency arrays, which is also the only rule name that appears 30 times.
See it
The one disable comment that names a Next.js rule guards a script you can read without any of our source:
curl -s https://cogniprep.app/ | grep -c 'window.localStorage && localStorage'
That returns 1. The homepage is 477,515 bytes with 106 inline <script> tags in it, and exactly one of them reads a stored theme and sets a class before the browser paints. Open cogniprep.app with view-source and it is in the head, synchronous, ahead of the stylesheets. The comment sitting on it in our repo names a rule that was never going to fire, about an attribute the tag does not have, for a linter we do not run, and it is still the most useful thing written near that line.
Count your own disables before you install the linter. The list tells you where the code already knows it is cheating.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.