// ESLint flat config. Static analysis for make check; formatting stays with // prettier (script/fmt-check), so nothing here touches style. // // The sources are CommonJS and are bundled per entrypoint by build.js, so the // globals differ by tree and are declared per tree below. Getting that wrong in // either direction defeats the point: too few globals buries a real no-undef in // false positives, too many hides the next unimported identifier. const js = require("@eslint/js"); const globals = require("globals"); // The extension APIs. MV3 Chrome exposes `chrome`; Firefox exposes both, and // the code feature-detects between them. const extensionGlobals = { chrome: "readonly", browser: "readonly", }; const commonjs = { ecmaVersion: 2024, sourceType: "commonjs", }; module.exports = [ { ignores: [ "dist/", "node_modules/", // Emitted by build.js, not authored here. "src/popup/styles/", ], }, js.configs.recommended, { rules: { // The two rules this config exists for. Both are already // error-level in the recommended set; restated so a future // recommended-set change cannot silently downgrade them. "no-undef": "error", // `_`-prefixed arguments are the deliberate "present for the // interface, unused here" marker: the popup views share one // init(ctx) signature and three of the eight do not read ctx. // An unused catch binding is written `catch {`, which the repo // already does, so caught errors stay checked. "no-unused-vars": ["error", { argsIgnorePattern: "^_" }], // Off: it flags `password = null` and `decryptedSecret = null` in // approval.js and confirmTx.js, which are the best-effort wipes of // decrypted key material after use. The assignments are dead by // construction — that is the point of them — and satisfying the // rule would mean deleting the wipes. "no-useless-assignment": "off", // Off: it requires every rethrow to carry `{ cause }`. That is a // change to what the wallet's error paths actually throw, which is // not this config's business; adopting it is its own decision. "preserve-caught-error": "off", }, }, // Popup and content scripts: page/window context. { files: ["src/popup/**/*.js", "src/content/**/*.js"], languageOptions: { ...commonjs, globals: { ...globals.browser, ...extensionGlobals }, }, }, // MV3 background: a service worker, with no window and no document. { files: ["src/background/**/*.js"], languageOptions: { ...commonjs, globals: { ...globals.serviceworker, ...extensionGlobals }, }, }, // src/shared is bundled into both, so it may only use what both provide: // the service worker globals are the intersection, plus the extension APIs. { files: ["src/shared/**/*.js"], languageOptions: { ...commonjs, globals: { ...globals.serviceworker, ...extensionGlobals }, }, }, // src/shared/ens.js is the documented exception to the line above: its own // header says POPUP ONLY, it caches in localStorage, and only popup views // require it. Linting it as a service worker would be wrong about the file. { files: ["src/shared/ens.js"], languageOptions: { ...commonjs, globals: { ...globals.browser, ...extensionGlobals }, }, }, // Unit tests: jest on node. { files: ["tests/**/*.test.js"], languageOptions: { ...commonjs, globals: { ...globals.node, ...globals.jest }, }, }, // The build script is a plain node program. { files: ["build.js"], languageOptions: { ...commonjs, globals: { ...globals.node }, }, }, // The e2e harnesses are node programs that also carry, inline, the // callbacks they ship into the browser via page.evaluate — so both // contexts really are present in the same file and both sets of globals // are in scope somewhere in it. { files: ["tests/e2e/**/*.js"], languageOptions: { ...commonjs, globals: { ...globals.node, ...globals.browser, ...extensionGlobals, }, }, }, // This config file itself. { files: ["eslint.config.js"], languageOptions: { ...commonjs, globals: { ...globals.node }, }, }, ];