build: add ESLint to script/lint and containerize linting (closes #152) #286
Reference in New Issue
Block a user
Delete Branch "issue-152-eslint"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #152.
script/lintranprettier --check ., byte for byte whatscript/fmt-checkruns, somake checkchecked formatting twice and did no static analysis on a cryptocurrency wallet.What changed
ESLint pinned in
package.json(eslint@10.8.1,@eslint/js@10.0.1,globals@17.11.0, all exact, all withyarn.lockintegrity entries; added withyarn, notnpm). Flat config ineslint.config.jswith@eslint/jsrecommended as the base.no-undefandno-unused-varsare restated error-level on top of the recommended set so a future recommended-set change cannot silently downgrade the two rules this exists for.Globals are declared per tree, not globally, because a too-wide set hides the next unimported identifier:
src/popup/**,src/content/**chrome/browsersrc/background/**,src/shared/**chrome/browsersrc/shared/ens.jschrome/browsertests/**/*.test.jsbuild.jstests/e2e/**chrome/browsersrc/shared/ens.jsis the one override insidesrc/shared/: its own header says POPUP ONLY, it caches inlocalStorage, and only popup views require it.tests/e2e/**gets both sets because those files carry the callbacks they ship into the page viapage.evaluate()inline, so both contexts really are present in the same file.package.json'slintis noweslint . && prettier --check ., soyarn run lintandmake lintagree.script/fmt-checkis untouched. No--fixanywhere in the lint path.Containerized, per the scope note on the issue
script/lintnow builds the Dockerfile's newlintstage, so the ESLint deciding whether this repo is green is the pinned one and not whatever the host happens to have.AUTISTMASK_LINT_NATIVE=1, set only in that image, is what makes themake checkrunning inside the CI build lint in place instead of recursing into a docker daemon it does not have. Thecheckstage takes aCOPY --from=lint /app/package.json /dev/nulldependency so BuildKit finishes lint before starting it and a lint failure fails the build early rather than racing it.script/lintuses--output=type=cacheonly: the exit status is the whole result and exporting an image afterwards cost about ten times the lint itself.Docker is now required to lint. That is the point, and it matches the docker-only lint policy.
Two rules narrowed, deliberately
no-useless-assignment: off. It flags thepassword = nullanddecryptedSecret = nullwipes inapproval.jsandconfirmTx.js(10 sites). Those assignments are dead by construction — that is what a best-effort wipe of decrypted key material is — and the rule's fix is to delete the wipe.preserve-caught-error: off. It requires every rethrow to carry{ cause }(4 sites). That changes what the wallet's error paths actually throw; adopting it is a decision of its own, not a side effect of turning a linter on.Both are commented in
eslint.config.jswith the reason.Violations fixed
53
no-undefand 41no-unused-vars, all by hand — nosed -i, no scripted rewrite.clearViewStack,formatUnits,showFlash,flashCopyFeedback,currentNetwork,NETWORKS,SUPPORTED_CHAIN_IDS,currentAddress,getAddressValueUsd,escapeHtml,saveState,getBytes.catch {, which the repo already used elsewhere, so caught errors stay checked rather than being excused by a config pattern.init(ctx)view signature keeps its parameter as_ctxin the three views that do not read it (approval,confirmTx,receive), matched byargsIgnorePattern: "^_". Uniform interface preserved, intent stated.transactionDetail.jsstored a module-levelctxnothing ever read, andloadFullTxDetails()took anisContractCallit never used from its single caller. Both removed.txStatus.js'setherscanTokenLink()was dead and superseded bytoAddressHtml. Removed.tests/e2e/run.js'swithTimeout(promise, name)droppedname; the caller passest.name, so it now appears in the timeout message as intended.src/shared/uniswap.js'sdecodeV2SwapExactOut()is kept behind a scopedeslint-disable-next-line. It is the decoder for Universal Router command0x09, anddecode()has no0x09arm, so a V2 exact-out swap shows its command name and no token or amount detail in the approval preview. Deleting it would widen that gap rather than close it, and wiring a new command arm is outside a lint-adoption change, so it is filed as #283 and the code the fix needs is still there.Demonstration that the linter is load-bearing
foo()added torenderTotalValue()insrc/popup/views/home.js, thenmake lint:And the same probe through
script/cibuild, to show the gate holds for CI and not just formake lint:The probe was reverted;
git diffonsrc/popup/views/home.jsin this branch shows only theflashCopyFeedbackimport removal.Verification
Rebased onto
nextat0be20d7(which landedsrc/popup/viewRouter.js, a file the linter had never seen), then re-run:make checkexit 0: 29 suites / 703 tests passed,test-verify-build: 18 case(s) passed, lint clean,All matched files use Prettier code style!. The lint layer executed rather than reportingCACHED— the run is timestamped#11 0.170 Linting...through#11 7.405 All matched files use Prettier code style!.make checkis non-mutating:git diff HEADhashed identical before and after, andgit status --porcelainempty at HEAD.script/cibuildexit 0 on the restructured multi-stage Dockerfile, throughmake check,make buildandverify-build: 4 bundle(s) verified autistmask-build-debug=off.make fmtrun; markdown and JS committed formatted.Docs
The README "Entrypoints" entry for
script/lintno longer said what it does, and is rewritten to state both tools, the non-mutating guarantee, and the docker requirement. Two claims elsewhere are now false and are corrected: the README end-to-end section and thescript/test-e2eheader both said a used-but-not-imported identifier is invisible tomake check.TODO.mdmoved on in the same commit.Not done here
script/testandscript/fmt-checkstill run on the host. Only lint is containerized, which is what the scope note asked for; whether the rest should follow is a separate question.script/lint ran `prettier --check .`, byte for byte what script/fmt-check runs, so make check checked formatting twice and did no static analysis on a cryptocurrency wallet. Two used-but-not-imported crashes shipped past it. ESLint is pinned in package.json with @eslint/js recommended as the base and a flat config in eslint.config.js. no-undef and no-unused-vars are restated error-level so a future recommended-set change cannot downgrade them. Globals are declared per tree rather than globally, because a too-wide set hides the next unimported identifier: browser for the popup and content scripts, service worker for src/background/ and src/shared/, browser for the one documented POPUP ONLY module in src/shared/, jest for tests/, node for build.js, and both for the e2e harnesses, which carry the callbacks they ship into the page inline. Two rules new to the recommended set are off, and both would have cost something to satisfy. no-useless-assignment flags the `password = null` and `decryptedSecret = null` wipes in approval.js and confirmTx.js: those assignments are dead by construction, which is the point of them, and the rule's fix is to delete the wipe. preserve-caught-error would change what the wallet's error paths throw, which is a decision of its own. Every remaining violation is fixed: 41 unused bindings and 53 undefined identifiers. Unused catch bindings became `catch {`, which the repo already used; the shared init(ctx) view signature keeps its parameter as _ctx in the three views that do not read it. src/shared/uniswap.js keeps its unused V2_SWAP_EXACT_OUT decoder behind a scoped disable, because deleting it would widen the gap it represents rather than close it (#283). Linting is containerized. script/lint builds the Dockerfile's new lint stage so the ESLint deciding whether this repo is green is the pinned one and not whatever the host has; AUTISTMASK_LINT_NATIVE, set only in that image, is what makes make check inside the CI build lint in place instead of recursing into docker. The check stage takes a COPY --from=lint dependency so a lint failure fails the whole build early rather than racing it. No --fix anywhere in the lint path: make check remains non-mutating. The README claim that a used-but-not-imported identifier is invisible to make check, and the same claim in script/test-e2e, are no longer true and are corrected.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.