script/lint runs prettier --check . (package.json:11), which is
byte-identical to script/fmt-check (package.json:13). So make check runs
the same formatting check twice and performs no static analysis at all.
This repo is a cryptocurrency wallet and has no linter.
Two hard, shipped, user-facing crashes on main are the direct consequence,
both of the same class (identifier used but not imported):
AddToken is entirely unreachable (addToken.js:24 uses showView, never
imported) — filed separately.
TransactionDetail crashes for every ERC-20 transfer
(transactionDetail.js:136 uses addressDotHtml, never imported) — filed
separately.
make check is green on main despite both. A linter with no-undef would
have caught each one at commit time. The CI workflow cannot catch them either,
because nothing exercises the views.
There is also a substantial tail of unused imports that a linter would flag: src/popup/index.js:15 (clearViewStack); confirmTx.js (formatUnits, showFlash, flashCopyFeedback, escapeHtml, currentNetwork); settings.js:13 (NETWORKS, SUPPORTED_CHAIN_IDS); addressToken.js
(currentAddress, getAddressValueUsd); addressDetail.js
(currentAddress); approval.js (currentNetwork); home.js
(flashCopyFeedback); send.js (escapeHtml); txStatus.js (saveState); helpers.js:7 (getAddressValueUsd).
Implementation requirements
Add ESLint as a devDependency via yarn (never npm). Use the modern flat
config (eslint.config.js). Per the global convention, prefer the widely
used, well maintained standard: @eslint/js recommended as the base.
Rules that must be enabled and must fail the build:
no-undef (the rule that catches both live crashes)
no-unused-vars (catches the dead-import tail above)
Configure the right environments/globals so there are no false positives:
the popup and content scripts are browser context, src/background/ is a
service worker, the sources use CommonJS require/module.exports, and tests/ is jest. build.js is Node. Set ecmaVersion appropriately.
Wire it into script/lintin addition to the existing prettier check —
do not drop formatting enforcement. Keep script/fmt-check as it is. package.json's lint script should invoke eslint too, so yarn run lint and make lint agree.
Fix every violation the new linter reports. Removing genuinely unused
imports is in scope for this issue. If a rule produces a large volume of
unrelated churn, narrow the rule rather than mass-editing unrelated code,
and say so in the PR.
Do not use scripted search-and-replace to strip the unused imports; edit the
files directly.
Respect the ordering: the two point-fix crash issues may land before or
after this, but after this issue lands, both classes must be
linter-detectable. If they have already landed, confirm the linter stays
green; if not, this issue is expected to surface them.
make check must remain non-mutating (REPO_POLICIES) — use --fix only
manually, never inside script/lint.
Definition of done
eslint.config.js exists, ESLint is a pinned devDependency in package.json with a matching yarn.lock integrity entry.
make lint runs both ESLint and prettier and fails on either.
no-undef and no-unused-vars are active and error-level.
Deliberately introducing foo() with no import into any src/ file
makes make lint fail. Demonstrate this in the PR description.
Zero ESLint violations remain in src/, tests/, and build.js.
make check passes and modifies no files in the working tree.
README "Entrypoints" section still accurately describes script/lint.
TODO.md updated in the same commit.
## Problem
`script/lint` runs `prettier --check .` (`package.json:11`), which is
byte-identical to `script/fmt-check` (`package.json:13`). So `make check` runs
the same formatting check twice and performs **no static analysis at all**.
This repo is a cryptocurrency wallet and has no linter.
Two hard, shipped, user-facing crashes on `main` are the direct consequence,
both of the same class (identifier used but not imported):
- AddToken is entirely unreachable (`addToken.js:24` uses `showView`, never
imported) — filed separately.
- TransactionDetail crashes for every ERC-20 transfer
(`transactionDetail.js:136` uses `addressDotHtml`, never imported) — filed
separately.
`make check` is green on `main` despite both. A linter with `no-undef` would
have caught each one at commit time. The CI workflow cannot catch them either,
because nothing exercises the views.
There is also a substantial tail of unused imports that a linter would flag:
`src/popup/index.js:15` (`clearViewStack`); `confirmTx.js` (`formatUnits`,
`showFlash`, `flashCopyFeedback`, `escapeHtml`, `currentNetwork`);
`settings.js:13` (`NETWORKS`, `SUPPORTED_CHAIN_IDS`); `addressToken.js`
(`currentAddress`, `getAddressValueUsd`); `addressDetail.js`
(`currentAddress`); `approval.js` (`currentNetwork`); `home.js`
(`flashCopyFeedback`); `send.js` (`escapeHtml`); `txStatus.js` (`saveState`);
`helpers.js:7` (`getAddressValueUsd`).
## Implementation requirements
- Add ESLint as a devDependency via `yarn` (never `npm`). Use the modern flat
config (`eslint.config.js`). Per the global convention, prefer the widely
used, well maintained standard: `@eslint/js` recommended as the base.
- Rules that must be enabled and must fail the build:
- `no-undef` (the rule that catches both live crashes)
- `no-unused-vars` (catches the dead-import tail above)
- Configure the right environments/globals so there are no false positives:
the popup and content scripts are browser context, `src/background/` is a
service worker, the sources use CommonJS `require`/`module.exports`, and
`tests/` is jest. `build.js` is Node. Set `ecmaVersion` appropriately.
- Wire it into `script/lint` **in addition to** the existing prettier check —
do not drop formatting enforcement. Keep `script/fmt-check` as it is.
`package.json`'s `lint` script should invoke eslint too, so
`yarn run lint` and `make lint` agree.
- Fix every violation the new linter reports. Removing genuinely unused
imports is in scope for this issue. If a rule produces a large volume of
unrelated churn, narrow the rule rather than mass-editing unrelated code,
and say so in the PR.
- Do not use scripted search-and-replace to strip the unused imports; edit the
files directly.
- Respect the ordering: the two point-fix crash issues may land before or
after this, but after this issue lands, both classes must be
linter-detectable. If they have already landed, confirm the linter stays
green; if not, this issue is expected to surface them.
- `make check` must remain non-mutating (REPO_POLICIES) — use `--fix` only
manually, never inside `script/lint`.
## Definition of done
- [ ] `eslint.config.js` exists, ESLint is a pinned devDependency in
`package.json` with a matching `yarn.lock` integrity entry.
- [ ] `make lint` runs both ESLint and prettier and fails on either.
- [ ] `no-undef` and `no-unused-vars` are active and error-level.
- [ ] Deliberately introducing `foo()` with no import into any `src/` file
makes `make lint` fail. Demonstrate this in the PR description.
- [ ] Zero ESLint violations remain in `src/`, `tests/`, and `build.js`.
- [ ] `make check` passes and modifies no files in the working tree.
- [ ] README "Entrypoints" section still accurately describes `script/lint`.
- [ ] `TODO.md` updated in the same commit.
clawbot
added this to the 1.0.0 milestone 2026-08-09 03:42:24 +02:00
Scope note, found while landing #206: script/lint is yarn run lint (prettier --check .) on the host and does not containerize. The only containerized path is script/cibuild, whose Dockerfile runs make check.
That is tolerable while lint is prettier-only, but it stops being tolerable when this issue puts ESLint behind that target: results would then depend on whichever ESLint the host happens to have, which is the exact class of drift the docker-only lint policy exists to prevent.
So this unit should containerize script/lint as well as add ESLint to it, and make check must reach the containerized path rather than the host one.
Scope note, found while landing https://git.eeqj.de/sneak/AutistMask/pulls/206: `script/lint` is `yarn run lint` (`prettier --check .`) on the **host** and does not containerize. The only containerized path is `script/cibuild`, whose Dockerfile runs `make check`.
That is tolerable while lint is prettier-only, but it stops being tolerable when this issue puts ESLint behind that target: results would then depend on whichever ESLint the host happens to have, which is the exact class of drift the docker-only lint policy exists to prevent.
So this unit should containerize `script/lint` as well as add ESLint to it, and `make check` must reach the containerized path rather than the host one.
eslint.config.js (flat), @eslint/js recommended as base, sourceType: "commonjs", ecmaVersion current. Per-tree globals so there are no false positives: src/popup/** + src/content/** browser, src/background/** service worker, src/shared/** the intersection both actually use, tests/** jest + node, build.js + tests/e2e/** node. no-undef and no-unused-vars error-level; dist/, node_modules/, src/shared/phishingBlocklist.json ignored.
Fix every violation by hand, file by file — no scripted rewrites. Expected shape is the unused-import tail in the body plus whatever no-undef finds beyond it. If a rule turns out to generate churn unrelated to this issue I will narrow the rule and say so in the PR rather than mass-edit.
package.jsonlint becomes eslint + the existing prettier --check ., so yarn run lint and make lint agree. script/fmt-check untouched, no --fix anywhere in the lint path, make check stays non-mutating.
Per the scope note above, script/lint gets containerized: a lint stage in the Dockerfile on the already-pinned node base, script/lint running docker build --target lint from the host, and an explicit env guard set in the image so the make check that runs inside the CI build takes the native path instead of recursing into docker. make check therefore reaches the containerized linter, not the host's ESLint.
Demonstrate the linter is load-bearing: add an unimported foo() call to a src/ file, capture the failing make lint output into the PR body, revert.
README Entrypoints line for script/lint updated to say what it now runs and that it containerizes; TODO.md moved on in the same commit.
Plan, on `issue-152-eslint`:
1. `yarn add --dev` pinned `eslint` + `@eslint/js` (exact versions, `yarn.lock` integrity entries).
2. `eslint.config.js` (flat), `@eslint/js` recommended as base, `sourceType: "commonjs"`, `ecmaVersion` current. Per-tree globals so there are no false positives: `src/popup/**` + `src/content/**` browser, `src/background/**` service worker, `src/shared/**` the intersection both actually use, `tests/**` jest + node, `build.js` + `tests/e2e/**` node. `no-undef` and `no-unused-vars` error-level; `dist/`, `node_modules/`, `src/shared/phishingBlocklist.json` ignored.
3. Fix every violation by hand, file by file — no scripted rewrites. Expected shape is the unused-import tail in the body plus whatever `no-undef` finds beyond it. If a rule turns out to generate churn unrelated to this issue I will narrow the rule and say so in the PR rather than mass-edit.
4. `package.json` `lint` becomes eslint + the existing `prettier --check .`, so `yarn run lint` and `make lint` agree. `script/fmt-check` untouched, no `--fix` anywhere in the lint path, `make check` stays non-mutating.
5. Per the scope note above, `script/lint` gets containerized: a `lint` stage in the `Dockerfile` on the already-pinned node base, `script/lint` running `docker build --target lint` from the host, and an explicit env guard set in the image so the `make check` that runs *inside* the CI build takes the native path instead of recursing into docker. `make check` therefore reaches the containerized linter, not the host's ESLint.
6. Demonstrate the linter is load-bearing: add an unimported `foo()` call to a `src/` file, capture the failing `make lint` output into the PR body, revert.
7. README Entrypoints line for `script/lint` updated to say what it now runs and that it containerizes; `TODO.md` moved on in the same commit.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
script/lintrunsprettier --check .(package.json:11), which isbyte-identical to
script/fmt-check(package.json:13). Somake checkrunsthe same formatting check twice and performs no static analysis at all.
This repo is a cryptocurrency wallet and has no linter.
Two hard, shipped, user-facing crashes on
mainare the direct consequence,both of the same class (identifier used but not imported):
addToken.js:24usesshowView, neverimported) — filed separately.
(
transactionDetail.js:136usesaddressDotHtml, never imported) — filedseparately.
make checkis green onmaindespite both. A linter withno-undefwouldhave caught each one at commit time. The CI workflow cannot catch them either,
because nothing exercises the views.
There is also a substantial tail of unused imports that a linter would flag:
src/popup/index.js:15(clearViewStack);confirmTx.js(formatUnits,showFlash,flashCopyFeedback,escapeHtml,currentNetwork);settings.js:13(NETWORKS,SUPPORTED_CHAIN_IDS);addressToken.js(
currentAddress,getAddressValueUsd);addressDetail.js(
currentAddress);approval.js(currentNetwork);home.js(
flashCopyFeedback);send.js(escapeHtml);txStatus.js(saveState);helpers.js:7(getAddressValueUsd).Implementation requirements
yarn(nevernpm). Use the modern flatconfig (
eslint.config.js). Per the global convention, prefer the widelyused, well maintained standard:
@eslint/jsrecommended as the base.no-undef(the rule that catches both live crashes)no-unused-vars(catches the dead-import tail above)the popup and content scripts are browser context,
src/background/is aservice worker, the sources use CommonJS
require/module.exports, andtests/is jest.build.jsis Node. SetecmaVersionappropriately.script/lintin addition to the existing prettier check —do not drop formatting enforcement. Keep
script/fmt-checkas it is.package.json'slintscript should invoke eslint too, soyarn run lintandmake lintagree.imports is in scope for this issue. If a rule produces a large volume of
unrelated churn, narrow the rule rather than mass-editing unrelated code,
and say so in the PR.
files directly.
after this, but after this issue lands, both classes must be
linter-detectable. If they have already landed, confirm the linter stays
green; if not, this issue is expected to surface them.
make checkmust remain non-mutating (REPO_POLICIES) — use--fixonlymanually, never inside
script/lint.Definition of done
eslint.config.jsexists, ESLint is a pinned devDependency inpackage.jsonwith a matchingyarn.lockintegrity entry.make lintruns both ESLint and prettier and fails on either.no-undefandno-unused-varsare active and error-level.foo()with no import into anysrc/filemakes
make lintfail. Demonstrate this in the PR description.src/,tests/, andbuild.js.make checkpasses and modifies no files in the working tree.script/lint.TODO.mdupdated in the same commit.clawbot referenced this issue2026-08-09 07:08:12 +02:00
Scope note, found while landing #206:
script/lintisyarn run lint(prettier --check .) on the host and does not containerize. The only containerized path isscript/cibuild, whose Dockerfile runsmake check.That is tolerable while lint is prettier-only, but it stops being tolerable when this issue puts ESLint behind that target: results would then depend on whichever ESLint the host happens to have, which is the exact class of drift the docker-only lint policy exists to prevent.
So this unit should containerize
script/lintas well as add ESLint to it, andmake checkmust reach the containerized path rather than the host one.Plan, on
issue-152-eslint:yarn add --devpinnedeslint+@eslint/js(exact versions,yarn.lockintegrity entries).eslint.config.js(flat),@eslint/jsrecommended as base,sourceType: "commonjs",ecmaVersioncurrent. Per-tree globals so there are no false positives:src/popup/**+src/content/**browser,src/background/**service worker,src/shared/**the intersection both actually use,tests/**jest + node,build.js+tests/e2e/**node.no-undefandno-unused-varserror-level;dist/,node_modules/,src/shared/phishingBlocklist.jsonignored.no-undeffinds beyond it. If a rule turns out to generate churn unrelated to this issue I will narrow the rule and say so in the PR rather than mass-edit.package.jsonlintbecomes eslint + the existingprettier --check ., soyarn run lintandmake lintagree.script/fmt-checkuntouched, no--fixanywhere in the lint path,make checkstays non-mutating.script/lintgets containerized: alintstage in theDockerfileon the already-pinned node base,script/lintrunningdocker build --target lintfrom the host, and an explicit env guard set in the image so themake checkthat runs inside the CI build takes the native path instead of recursing into docker.make checktherefore reaches the containerized linter, not the host's ESLint.foo()call to asrc/file, capture the failingmake lintoutput into the PR body, revert.script/lintupdated to say what it now runs and that it containerizes;TODO.mdmoved on in the same commit.