Skip to content

Switch ui/ linting from eslint to biome#8680

Open
Eijebong wants to merge 24 commits into
taskcluster:mainfrom
Eijebong:biome
Open

Switch ui/ linting from eslint to biome#8680
Eijebong wants to merge 24 commits into
taskcluster:mainfrom
Eijebong:biome

Conversation

@Eijebong
Copy link
Copy Markdown
Contributor

This should be reviewed commit by commit. I tried keeping lint fixes split up and justified.

Note that a lot of issues discovered during this migration have already been merged, the fixes in here are tiny enough that I didn't feel like they warranted their own PR.

The first commit does the scaffolding, switches eslint to biome with a configuration that matches somewhat. The two commits after that are autogenerated with biome. Each commit after that fixes/silences specific lints.

Part of #8372, doesn't close it yet as we're still using eslint in services/clients.

@Eijebong Eijebong requested a review from a team as a code owner May 22, 2026 14:08
@Eijebong Eijebong requested review from lotas, matt-boris and petemoore and removed request for a team May 22, 2026 14:08
@github-project-automation github-project-automation Bot moved this to Backlog / Inbox in TC intake board May 22, 2026
Eijebong added 23 commits May 22, 2026 16:33
Eslint's ecosystem has been very unmaintained for quite some time, it
brings in a lot of dependency that have open CVEs and despite those not
affecting us in a way that matters, they're noisy and are hard to trace
back to the linter. On top of that, eslint is *slow*, the config is
quite arduous to migrate over newer versions. Instead of paying the
price of that when updating to the latest we could update to (which
would be one or two major version out of date since we have dependencies
on unmaintained eslint plugins that don't support the latest versions),
switch to biome.

I've already found and fixed a few bugs due to some of the lints that
biome has that eslint didn't. Biome is also faster (especially on first
runs).

Regarding the config, I kept it fairly simple, enabling the recommended
lints and disabling those that were giving false positives without
providing extra value:

- `useNodejsImportProtocol`: we polyfill things so this is all false positives

I also tried matching the formatter to our prettier config as best I
could although there's some differences that I couldn't avoid.

Note that while I said in 70b691a that
this work should allow us to remove the `get-intrinsic` resolution, it
turns out that nope, it cannot. Webpack-cli also brings in
v8-compile-cache which makes it fail in the same way. So we have to keep
the resolution until webpack is either updated or gone.
This formats the UI code with biome instead of prettier. Most of the
changes are because prettier really didn't like destructuring objects on
one line even if the lines wasn't going over the line size limit and
because biome really wants `() =>` to be on its own line. There's no
config option to match those between prettier/biome so we have to eat
the difference.
This applies all fixes that biome considers safe. (`noUselessThisAlias`,
`noPrototypeBuiltins`, `useRegexLiterals`). Those are all very tiny
changes so I decided to bundle them together in the same commit.
Biome complains about `useExhaustiveDependencies` in two places where
the dependency list for `useEffect` is not evident. They're both safe to
ignore.
This is just a translation of `eslint-disable` to `biome-ignore`.
This renames a bunch of function parameters by prepending a `_` to them.
I've checked them all and they're all legitimately unused.
The `match?.params` here (added back in
1372614) would crash if `match` was
actually undefined as it'd resolve to `const { taskId } = undefined`.

In practice, `match` is always provided by `react-router` *and* we
already use it unconditionally on line 24: `taskId:
props.match.params.taskId`. Since it's useless, just remove the optional
chaining to make biome happy.
In practice this is going to make no difference whatsoever. But biome
calls it out because in theory you could get an array from a different
domain (an iframe), which would get checked against the Array prototype
of the current domain and differ. In this case, that's never going to
happen, but this follows what MDN
[recommends](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#instanceof_vs._array.isarray),
works, and silences a biome warning.
Biome is a bit too trigger happy on this lint and complains about our
JSON-e since it's creating objects with a `then` property which could
indeed be a problem later on if those were thenable. Since they're
static objects that get resolved and never get awaited, we don't care
and they're safe to ignore.
Biome was complaining that the `<Link>` side of the ternary was missing
a `key` (rightfully so). The other branch already had a
`key={indexName}` but while testing this I realized that having an index
path like: `foo.foo.foo` would duplicate keys in the breadcrumb which
should lead to react being unhappy with us and emitting warnings. It
turned out in practice not to be an issue because the component is used
in a breadcrumb which rewrites the `key` anyway to `child-{i}`. I
switched the key on both sides anyway to `{path}` which is guaranteed to
be unique and stable. It fixes the warning and will avoid someone else
getting tripped by this as well as stop depending on an internal MUI
behavior for this to be correct.
Biome flagged the `toString` import from ramda as shadowing the global
`toString`. The import was only used to produce a stable key for a
React `<Fragment>`, which I just replaced with `JSON.stringify(scope)`
which basically does the same thing. This drops the ramda import and
silences the warning.
There were 4 reduces that were copying the accumulator content in reduce
on each step instead of mutating it which made it `O(n^2)` instead of
`O(n)`. I'm not expecting any of these to make a real world performance
difference but this is the same, is faster and silences biome.
It provides no value on the page and is just cosmetic.

Fixes a `a11y/noSvgWithoutTitle` biome lint tripup.
This fixes a biome `a11y/noSvgWithoutTitle` lint. I don't think "Trend
graph" is a great title, but the alternatives are marking those as
`aria-hidden` or threading in the title of the graph which has no real
upside since the SVG content is not describable anyway. I chose to keep
the title because I think it's confusing to get "Last 24h" -> *Nothing*
instead of "Last 24h" -> "Trend graph" but I'm by no mean an
accessibility expert and would be ok with changing that, should anyone
voice any concern.
This fixes 2 lints regarding accessibility on this element,
`a11y/useSemanticElements` and `a11y/useKeyWithClickEvents`. The
result looks exactly the same and now works with keyboard navigation
The alt is provided through props. We could technically add it as a
prop, destructure props with a default of `''` for alt and use that, but
that means that an image with no alt would end up with `alt = ""` which
I don't think is better. So just keep the previous behavior and silence
the lint.
It provides no value on the page and is just cosmetic.

Fixes a `a11y/noSvgWithoutTitle` biome lint.
Switch the `desc` to a `title` which pretty much does the same (`title` is
mandatory, `desc` can be used to describe more if needed)
Biome flags the bars as being static elements with a hover/click but not
having keyboard interactions. Making those keyboard interactable means
marking each bar as a button + tabIndex which means keyboard navigation
would require pressing tab 200+ times to get past the graph which is way
worse than not being able to interact with the graph. Especially given
the tasks are accessible below.
Since we're using virtualization here we can't use a proper `<table>`
element which biome gets angry about. This is a virtualization issue and
we make this accessible by setting the role manually instead of using
the "right" element. Just ignore the lint.
This isn't enabled by default so enable it and mark the various calls as
ignored.
This finalizes the migration from eslint to biome. There's a lot of
removed annotations in here that don't have a matching biome rule.
However, some of these were already not exercised by eslint since the
migration off of neutrino, and the remaining ones are not in biome
because they're only valid with old versions of react. Updating to biome
is part of my journey towards getting us to a more recent set of
dependency, so let's accept the gap for now. Some of these lints are
also superseded by the formatter or opinionated choices made by
eslint.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog / Inbox

Development

Successfully merging this pull request may close these issues.

1 participant