Skip to content

functions: move lint to Biome and fix every warning - #1709

Merged
HugoGresse merged 3 commits into
mainfrom
claude/issue-1406-functions-lint-4d2805
Nov 1, 2026
Merged

HugoGresse merged 3 commits into
mainfrom
claude/issue-1406-functions-lint-4d2805

Conversation

@HugoGresse

Copy link
Copy Markdown
Owner

Closes #1406

Why

npm run lint in functions/ did nothing: the script was "". ESLint 9 no longer reads .eslintrc.js, and eslint-config-google is no longer maintained. Running the old config by hand found about 120 warnings and 200 errors, many of them from rules that no longer exist.

#1700 moved the frontend to Biome, so functions/ now uses Biome too.

Tooling

  • Config: functions/biome.json uses the recommended preset with no rules turned off, and the same formatter settings as the root biome.jsonc.
    • It is a separate root: true config because functions/ is a standalone package, and the root config excludes it.
    • Biome is pinned at 2.6.4, the latest release. The root is still on 2.5.15.
  • Scripts:
    • npm run lint runs biome check --error-on-warnings (lint and format).
    • npm run format writes the formatting.
    • npm run typecheck type-checks the specs and test utilities, which the build excludes.
  • CI: build.yml gains a lint functions/ step and a typecheck functions/ specs step. Both fail on any new warning or type error.
  • Pre-commit: functions/package.json gets a lint-staged entry. lint-staged uses the nearest config, so staged functions files are now formatted and linted, with the same --error-on-warnings as CI.
  • Removed: the ESLint dependencies, .eslintrc.js, the dead tslint.json and tsconfig.dev.json. The unused tsconfig.test.json is reused as the spec typecheck config, with bundler resolution like vitest.

Warnings fixed

Biome reported 184 diagnostics: 3 errors, 177 warnings and 4 infos. All are fixed with real code changes, and nothing is disabled.

Rule Count Fix
useImportType 95 import type (safe autofix; the emitted JS is identical)
noExplicitAny 55 Real types, listed below
noUnusedFunctionParameters / noUnusedVariables 13 Removed, or _-prefixed where the position is fixed (Fastify handlers, SDK hooks)
useOptionalChain 7 ?. (same results for every value the types allow)
noStaticOnlyClass 5 The *Dao classes are now plain objects. Call sites and vi.spyOn(XDao, …) are unchanged.
useTemplate, useNodejsImportProtocol, useConst, noTsIgnore, noNonNullAssertion, noImplicitAnyLet, useIterableCallbackReturn, noExportsInTest 10 Direct fixes

The any fixes:

  • AggregatedVote is now typed. Each vote item holds either a count or text votes keyed by vote id. It is read through getAggregatedTextVotes, which vote.ts uses for the up-vote and deletion checks.
  • send() resolves to a Response. It never resolved to an Error, so the redundant instanceof Response checks are gone.
  • Error handler: it uses FastifyError.
  • Invites: applyInvites results are typed as WriteResult, and a redundant promise wrapper is removed.
  • Specs: they use FastifyInstance, vi.mocked(send) and firestore-vitest's FakeFirestoreDatabase.
  • Firestore test stub: it is typed. It now returns a missing document, instead of throwing, when the test SDK passes empty event data.

The new spec typecheck also found 4 type errors that already existed in mutations.spec.ts and resources.spec.ts. They come from gaps in the firestore-vitest typings and are fixed, with a comment each.

Behaviour

No runtime change is intended:

  • main and this branch were both compiled. The JS differs only where expected: object-literal DAOs, optional chains, template literals, node:crypto, Object.entries, and the vote helper.
  • vote.spec.ts covers the vote aggregation paths touched by the AggregatedVote typing. Its Vote cases also pass against main's logic.

Checks

On Node 24, from functions/:

  • npm run lint: 0 diagnostics.
  • npm run typecheck: clean, and it does report an error deliberately added to a spec.
  • npm run build: passes.
  • npx vitest run: 17 files, 272 tests pass, through both the functions config and the root config.

The Firestore rules specs in config/ were not run locally because the emulator port was taken. They are untouched.

/code-review max was run, and its findings are fixed in the last commit.

Needs a decision

  • Lint now gates deploys. The firebase.json functions predeploy runs npm run lint, so a functions deploy now stops on any lint warning. Before, it ran nothing.
  • Info diagnostics don't fail lint. --error-on-warnings ignores info-level suggestions such as useTemplate. Making them fail would mean raising those rules' severity in functions/biome.json.
  • Biome version. functions/ pins Biome 2.6.4 while the root uses 2.5.15. Dependabot's grouped monthly PR could go red if a new minor release adds a warning-level rule.
  • Swallowed errors. UserDao.getUsersByIds swallows read errors with no log, the same as before. Adding a console.warn would change runtime output, so it is left out.
  • Follow-ups found during review, all pre-existing:
    • sendContactEmail answers a failed send with HTTP 500 and success: true. It also puts unescaped form input into the email HTML.
    • exportEventVotes reads the session votes with its own casts, so it could share AggregatedVote once the type moves to functions/src/types.
    • firebase-tools is loaded eagerly by deleteProject, about 1.2s on every cold start.

🤖 Generated with Claude Code

HugoGresse and others added 3 commits November 1, 2026 13:08
The functions lint script was empty: ESLint 9 no longer reads the
.eslintrc.js config, and eslint-config-google is unmaintained. Move
functions/ to Biome like the frontend (#1700), with its own config since
functions/ is a standalone package. `npm run lint` checks lint and format
and fails on warnings; CI runs it, and lint-staged formats staged files.

Drop the dead tslint.json and tsconfig.dev.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- import types with `import type`
- replace `any` with real types: a typed AggregatedVote (count or text
  votes) read through getAggregatedTextVotes, send() resolving to a
  Response, FastifyInstance and vi.mocked() in specs, typed test stubs
- turn the static-only DAO classes into plain objects (same call sites)
- optional chains, template literals, node: protocol, unused parameters
- drop the stale eslint-disable comments

No runtime behaviour change; vote.spec.ts pins the vote aggregation
paths the AggregatedVote typing touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-ups:
- tsconfig.test.json (unused since the move to vitest) now typechecks the
  specs and test utilities the build excludes, with bundler resolution
  like vitest; `npm run typecheck` runs in CI. Fix the 4 spec type errors
  it reports (firestore-vitest typings gaps).
- The lint-staged hook fails on warnings like CI and the predeploy do.
- The test SDK passes the document name for empty event data: the
  firestore stub now returns a missing document instead of throwing.
- Use FastifyError, FakeFirestoreDatabase and WriteResult instead of
  hand-made or unknown types; drop the redundant applyInvites promise
  wrapper and the `instanceof Response` checks send() made redundant.
- getAggregatedTextVotes ignores a null vote item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

Preview for 8088002 (sandbox project open-feedback-hugo, expires in 7 days): https://open-feedback-hugo--pr-1709-rlyyhrft.web.app

@cypress

cypress Bot commented Nov 1, 2026

Copy link
Copy Markdown

open-feedback    Run #3148

Run Properties:  status check passed Passed #3148  •  git commit 80880023a6: fix(functions): typecheck the specs, align the hook with CI
Project open-feedback
Branch Review claude/issue-1406-functions-lint-4d2805
Run status status check passed Passed #3148
Run duration 02m 26s
Commit git commit 80880023a6: fix(functions): typecheck the specs, align the hook with CI
Committer Hugo Gresse
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 15
View all changes introduced in this branch ↗︎

@HugoGresse
HugoGresse merged commit 531c955 into main Nov 1, 2026
14 checks passed
@HugoGresse
HugoGresse deleted the claude/issue-1406-functions-lint-4d2805 branch November 1, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix eslint warnings on functions/

1 participant