Skip to content

chore: migrate to eslint 9 flat config and format the codebase - #220

Merged
edospadoni merged 1 commit into
mainfrom
chore/eslint9-v3
Sep 18, 2026
Merged

edospadoni merged 1 commit into
mainfrom
chore/eslint9-v3

Conversation

@edospadoni

Copy link
Copy Markdown
Member

Rifà #215 partendo da main aggiornato (dopo il merge di #192 e #213), e nel farlo sblocca anche #205 e #206.

Closes #205
Closes #206

Il problema

ESLint era completamente rotto su main: .eslintrc.cjs estendeva react-app e react-app/jest, ma eslint-config-react-app non è mai stato installato. npm run lint falliva prima ancora di analizzare un file:

ESLint couldn't find the config "react-app" to extend from.

Per questo nessuno vedeva più né errori né problemi di formattazione.

La scelta

Invece di rattoppare la configurazione eslintrc su eslint 8, ho migrato a eslint 9 + flat config. Questo risolve la #215 e contemporaneamente sblocca le due major di Renovate ferme sul peer eslint >= 9:

Pacchetto Da A
eslint ^8.56.0 ^9.39.5
@electron-toolkit/eslint-config-ts ^1.0.1 ^3.1.0 (#206)
@electron-toolkit/eslint-config-prettier ^2.0.0 ^3.0.0 (#205)
eslint-plugin-react ^7.33.2 ^7.37.5 (supporto flat config)
eslint-plugin-react-hooks — ^5.2.0 (era referenziato ma mai installato)

.eslintrc.cjs e .eslintignore sono sostituiti da eslint.config.mjs.

Scelte di configurazione

Partivo da 190 errori e 3485 warning. Tre decisioni non ovvie:

  • Regole React limitate a src/renderer/**. rules-of-hooks segnalava 4 violazioni in AccountController, una classe del main process: useNethVoiceAPI e useLogin sono funzioni normali che si chiamano use*, non hook. Restringere lo scope è più corretto che spegnere la regola.
  • camelcase disattivata. Tutti e 41 i report erano identificatori snake_case vincolati dal contratto con il backend (speeddial_num, shared_groups, dst_cnam) o token del tema (extra_large, full_w). Non sono rinominabili.
  • react/prop-types disattivata, TypeScript già controlla le props.

Due trappole incontrate

Le segnalo perché non erano evidenti e sarebbero passate inosservate:

1. spaced-comment --fix distrugge le direttive triple-slash. Trasformava /// <reference types="vite/client" /> in // / <reference ... />, annullando la direttiva. Conseguenza: tutti gli import .svg smettevano di risolvere, perché le dichiarazioni le fornisce vite/client. Risolto con markers: ['/'] nella configurazione della regola.

2. Prettier può spostare un @ts-ignore lontano dall'errore che sopprime. In store.ts la direttiva copriva una riga che conteneva l'intera espressione; riformattando, la riga è stata spezzata in due e la soppressione è finita su quella sbagliata, facendo riemergere un errore di tipo zustand.

Correzioni oltre la formattazione

  • useNethVoiceAPI.ts: rimosso l'executor async ridondante attorno a logout. Il corpo risolveva già incondizionatamente in finally, quindi il comportamento è identico e non si perde nessun percorso di rejection.
  • logger.ts: il tipo Function sostituito da una firma esplicita.
  • main.ts, Modal/index.tsx: import duplicati uniti.
  • AboutModule.tsx: rimosso il pattern di destructuring vuoto.
  • usePresenceService.ts: rimosso un blocco finally vuoto.
  • ipcEvents.ts: documentato il catch volutamente silenzioso nel percorso di drag.

Verifica

  • npm run lint: 0 errori, 339 warning (nessuno bloccante: no-explicit-any, no-unused-vars, exhaustive-deps).
  • npm run typecheck e npm run build: verdi.
  • Verificato che il codice appena mergiato dalle fix: no windows resize after listen call close #192 (_programmaticBoundsUntil, listener resize, guardia isMaximized) e fix: user data corruption on unclean Windows shutdown #213 (writeJsonAtomic, fsyncSync, readJsonSafe, safeStartApp, handler globali) sia intatto dopo la riformattazione.
  • Le 11 soppressioni @ts-ignore/@ts-expect-error sono le stesse di main, nessuna persa per spostamento.

Fuori scope

Prettier segnala altri 14 file non coperti da eslint (workflow YAML, README.md, template di issue, renovate.json, index.html, index.css). Li ho lasciati fuori per non aggiungere rumore a un diff già di 140 file: npm run format li toccherebbe, ed è una condizione preesistente.

ESLint was entirely broken on main: .eslintrc.cjs extended 'react-app'
and 'react-app/jest' while eslint-config-react-app was never installed,
so 'npm run lint' failed before linting a single file.

Rather than repair the eslintrc setup on eslint 8, this migrates to
eslint 9 flat config, which also unblocks the two Renovate majors that
were stuck on the 'eslint >= 9' peer requirement:

- eslint ^8.56.0 -> ^9.39.5
- @electron-toolkit/eslint-config-ts ^1.0.1 -> ^3.1.0 (#206)
- @electron-toolkit/eslint-config-prettier ^2.0.0 -> ^3.0.0 (#205)
- eslint-plugin-react ^7.33.2 -> ^7.37.5 (flat config support)
- eslint-plugin-react-hooks ^5.2.0 (was referenced, never installed)

.eslintrc.cjs and .eslintignore are replaced by eslint.config.mjs.

Config decisions:

- React and react-hooks rules are scoped to src/renderer/**. The main
  process has plain functions named use* (useNethVoiceAPI, useLogin)
  that rules-of-hooks reported as hook violations in AccountController.
- camelcase is off. Every one of its 41 reports was a snake_case field
  fixed by the backend API contract (speeddial_num, shared_groups,
  dst_cnam) or a theme token (extra_large, full_w).
- react/prop-types is off, since TypeScript already checks props.
- spaced-comment carries markers: ['/'] so --fix cannot mangle
  TypeScript triple-slash directives into '// / <reference ... />',
  which silently broke the vite/client types behind every .svg import.

Beyond formatting, these lint errors needed real fixes:

- useNethVoiceAPI.ts: drop the redundant async Promise executor around
  logout; the body already resolved unconditionally in finally, so
  behaviour is unchanged and no rejection path is lost.
- logger.ts: replace the unsafe Function type with an explicit signature.
- main.ts, Modal/index.tsx: merge duplicate imports.
- AboutModule.tsx: drop the empty destructuring pattern.
- usePresenceService.ts: drop an empty finally block.
- ipcEvents.ts: document the intentionally silent catch in the drag path.
- store.ts: move a // @ts-ignore next to the expression it suppresses.
  Reformatting split the statement it used to cover onto two lines,
  which silently un-suppressed a zustand typing error.

'npm run lint' now reports 0 errors and 339 warnings. typecheck and
build are green, and the code merged from #192 and #213 is intact.

Supersedes #215.
@github-actions

Copy link
Copy Markdown

Automatic builds from https://github.com/NethServer/nethlink/actions/runs/35343672891.
Commit: 569aee6

Name Platform Link
win-app.exe Windows (x64) Link
macos-app-x64.dmg MacOS (x64) Link
macos-app-arm64.dmg MacOS (arm64) Link
linux-app.AppImage Linux (x64) Link

@edospadoni
edospadoni merged commit 3951162 into main Sep 18, 2026
4 checks passed
edospadoni added a commit that referenced this pull request Sep 18, 2026
Rebased onto main after the eslint 9 migration (#220). The three original
commits are squashed into one: they were authored against the unformatted
tree, so replaying them individually onto the reformatted main conflicted
in six files with nothing but whitespace. Formatting both sides with the
same config first and transferring the resulting diff keeps the change set
identical to what was reviewed (+515/-116 against main, vs +458/-106
before) without hand-resolving formatting noise.

Squashed from:
  37dfa9c feat(auth): two-step login with Single Sign-On support
  7a3f336 feat(auth): extend SSO support to OIDC
  bf1b88b feat(auth): show a clear error when an SSO user is not enabled on CTI

Conflict resolved during the rebase: the IPC_EVENTS enum in
src/shared/constants.ts gained GET_HOST_CONFIG, SET_HOST_CONFIG,
SSO_LOGIN, SSO_LOGIN_RESULT and RECONNECT_PHONE_ISLAND. None of the five
exist on main, so all were kept.

Still a draft: not ready to merge.
@edospadoni
edospadoni deleted the chore/eslint9-v3 branch September 25, 2026 07:09
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.

1 participant