Conversation
Every package declared eslint and @typescript-eslint as devDependencies and ran `eslint src/**/*.ts`, and no config file had ever existed anywhere in the tree. So `npm run lint` exited 2 in all five packages, and had since the repo was created -- CI never ran it, so nothing noticed. One flat config at the root (ESLint 9, typescript-eslint 8) replaces five copies of a toolchain that was never wired up. The ruleset is narrow on purpose: type checking is TypeScript's job and formatting is Prettier's, so what is left is the class of thing neither catches. no-explicit-any is a warning under a per-package --max-warnings budget, and CI now runs lint so it cannot rot again. Fixing the 29 errors this surfaced was mostly mechanical -- unused catch bindings became optional catch, two dead `const w = window as any` went away, two `let`s became `const`. Two are worth knowing about: - _measureJSExecution() discarded the result of its arithmetic loop, so the loop could legally be optimised away entirely -- which would drive mathOps toward zero and trip the "JS execution unusually fast" automation signal on an ordinary browser. It now accumulates into a recorded mathSink, the way the array loop already used arrayLen, and reports stringLen for the same reason. Both are additive keys on a record the consumer reads by name. - The HTMLFormElement.prototype.submit interception uses rest parameters and a closure instead of `arguments` and a `this` alias. submit() takes no arguments, so behaviour is unchanged.
Contributor
Author
|
CI did not run on this PR — the workflow triggers on Ran the exact CI sequence locally from a clean
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The state it was in
All five packages declared
eslint@^8.56and@typescript-eslint@^6as devDependencies and ran:There was no config file anywhere in the tree.
npm run lintexited 2 in every package, and had since the repo was created:CI runs
build,testandcheck:edge, so nothing was gating on it and nobody noticed. Five copies of a toolchain, zero lines linted.(The glob was also wrong. In
sh,src/**/*.tsexpands assrc/*/*.ts— it would have missed every file directly undersrc/even with a config present.)What this does
eslint.config.mjs), ESLint 9 +typescript-eslint8, hoisted to the root the wayprettier,tsupandtypescriptalready are. The five duplicated devDependency sets are gone.casethat falls through, an expression statement that does nothing. Generated files (*.generated.ts) are ignored: findings there aren't actionable, since the fix belongs in the Go generator and the file is overwritten on regeneration.no-explicit-anyis a warning under a per-package budget (eslint src --max-warnings N), currently 9/25/14/2/5. CI fails if the count grows, so a newanyneeds a real type or a deliberate decision to raise the number. Same ratchet shape the frontend uses for stylelint. Verified: adding one strayanyfails the build.CONTRIBUTING.mddocuments the budget and says not to add per-package configs.The 29 errors it surfaced
Mostly mechanical: 18 unused
catch (e)bindings became optional catch, two deadconst w = window as anyassignments went away, twolets becameconst, anddeclare global { namespace Express { … } }is now allowed by rule option (allowDeclarations) rather than an inline disable, since augmenting a framework's types is exactly what that escape hatch is for.Two are worth reading:
_measureJSExecution()had a loop that could be optimised away.Nothing consumed the result, so an engine is free to eliminate the loop — which drives
mathOpstoward zero, anddetectAutomation()flagsjsTime < 0.1as "JS execution unusually fast." The author already knew about this hazard one loop down (results.arrayLen = arr.length; // Ensure array is "used" to prevent optimization); the arithmetic and string loops just never got the same treatment. They now accumulate into recordedmathSink/stringLenvalues. Both are additive keys on a record whose only consumer readsmathOpsby name through an open index signature.The
form.submitinterception usedargumentsand athisalias. Rewritten with rest parameters and a closure — the replacement still has to be afunctionso it keeps the call site'sthis, but the collector instance now comes in through an arrow rather thanconst self = this.submit()takes no arguments, so behaviour is unchanged.One rule I dropped while writing this: I had added
no-void, then found it flags(e) => void this._handleClick(e)inwidget.ts— which is the correct way to mark a deliberate fire-and-forget. The rule was fighting the idiom the codebase already uses properly.Follow-up worth considering
Type-aware linting (
@typescript-eslint/no-floating-promisesand friends) is the highest-value ruleset for this codebase and needsparserOptions.projectService. Left out here to keep this reviewable — it will surface a real batch of findings and slow CI, and deserves its own PR.Verification
npm run lint(0 errors, 55 warnings, all within budget),npm run build,npm test(306 tests),npm run check:edge— all pass.