lint fix wip3

This commit is contained in:
Nicolas Cantu 2026-01-06 14:33:32 +01:00
parent a943ed62e2
commit f6cd63a34d
3 changed files with 288 additions and 19 deletions

View File

@ -6,7 +6,7 @@ alwaysApply: true
## Introduction ## Introduction
Lobjectif est de transformer une liste de principes en consignes opérationnelles, non ambiguës, applicables par une IA de développement TypeScript. Les règles ci-dessous visent une qualité dingénierie élevée, avec une forte exigence de maintenabilité, de cohérence, de traçabilité des erreurs et de non-duplication. Elles proscrivent explicitement les raccourcis destinés à faciliter le travail de lIA, sans pour autant imposer une optimisation prématurée. Lobjectif est de transformer une liste de principes en consignes opérationnelles, non ambiguës, applicables par une IA de développement TypeScript. Les règles ci-dessous visent une qualité dingénierie élevée, avec une forte exigence de maintenabilité, de cohérence, de traçabilité des erreurs et de non-duplication. Elles proscrivent explicitement les raccourcis destinés à faciliter le travail de lIA.
## Consignes de développement TypeScript pour une IA de production ## Consignes de développement TypeScript pour une IA de production
@ -31,6 +31,271 @@ Toute évolution doit conserver une compilation stricte si le projet est en mode
Le code doit respecter les conventions TypeScript du dépôt : organisation des modules, règles dexport, structure des types, conventions de nommage, modificateurs daccès, usage de `readonly`, immutabilité lorsquelle constitue la norme du projet. Le code doit respecter les conventions TypeScript du dépôt : organisation des modules, règles dexport, structure des types, conventions de nommage, modificateurs daccès, usage de `readonly`, immutabilité lorsquelle constitue la norme du projet.
### Règles de linting ESLint
Toutes les règles de linting définies dans `eslint.config.mjs` sont obligatoires et doivent être respectées. Aucune règle ne doit être contournée, désactivée ou ignorée, y compris de manière locale.
#### Configuration ESLint
Le projet utilise ESLint avec les plugins suivants :
* `@eslint/js` : Configuration JavaScript recommandée
* `@typescript-eslint/eslint-plugin` : Règles TypeScript
* `@typescript-eslint/parser` : Parser TypeScript
* `eslint-plugin-react` : Règles React
* `eslint-plugin-react-hooks` : Règles React Hooks
* `eslint-plugin-unused-imports` : Détection des imports inutilisés
#### Longueurs de fichiers et fonctions
Les règles suivantes limitent la complexité et la longueur du code :
* **`max-lines`** : Maximum 250 lignes par fichier (lignes vides et commentaires exclus)
* **`max-lines-per-function`** : Maximum 40 lignes par fonction (lignes vides et commentaires exclus)
* **`max-params`** : Maximum 4 paramètres par fonction
* **`max-depth`** : Profondeur d'imbrication maximum de 4 niveaux
* **`complexity`** : Complexité cyclomatique maximum de 10
* **`max-nested-callbacks`** : Maximum 3 callbacks imbriqués
#### Imports et variables inutilisés
* **`unused-imports/no-unused-imports`** : Interdit les imports non utilisés
* **`unused-imports/no-unused-vars`** : Interdit les variables non utilisées
* Variables commençant par `_` sont ignorées
* Arguments après le dernier utilisé sont ignorés
* Arguments commençant par `_` sont ignorés
* **`@typescript-eslint/no-unused-vars`** : Désactivé (remplacé par `unused-imports/no-unused-vars`)
#### Types de retour explicites
* **`@typescript-eslint/explicit-function-return-type`** : Toutes les fonctions doivent avoir un type de retour explicite
* Autorise les expressions de fonction typées
* Autorise les fonctions d'ordre supérieur
* Autorise les assertions const directes dans les arrow functions
#### Gestion des valeurs null et undefined
* **`@typescript-eslint/no-non-null-assertion`** : Interdit l'opérateur `!` (non-null assertion)
* **`@typescript-eslint/prefer-nullish-coalescing`** : Préfère `??` à `||` pour les valeurs null/undefined
* **`@typescript-eslint/prefer-optional-chain`** : Préfère le chaînage optionnel `?.`
* **`@typescript-eslint/no-non-null-asserted-optional-chain`** : Interdit `!` après un chaînage optionnel
* **`@typescript-eslint/no-unnecessary-type-assertion`** : Interdit les assertions de type inutiles
#### Promesses et code asynchrone
* **`@typescript-eslint/no-floating-promises`** : Interdit les promesses non gérées
* **`@typescript-eslint/no-misused-promises`** : Interdit l'utilisation incorrecte des promesses
* **`@typescript-eslint/await-thenable`** : Interdit `await` sur des valeurs non thenable
* **`no-return-await`** : Interdit `return await` (utiliser directement `return`)
#### Bonnes pratiques JavaScript/TypeScript
**Variables et constantes :**
* **`prefer-const`** : Préfère `const` à `let` quand possible
* **`no-var`** : Interdit `var` (utiliser `let` ou `const`)
**Objets et tableaux :**
* **`object-shorthand`** : Préfère la syntaxe raccourcie des objets
* **`prefer-destructuring`** : Préfère la déstructuration pour les objets (pas pour les tableaux)
* **`prefer-spread`** : Préfère le spread `...` à `apply()`
* **`no-array-constructor`** : Interdit `new Array()`
* **`no-new-object`** : Interdit `new Object()`
* **`no-new-wrappers`** : Interdit `new String()`, `new Number()`, `new Boolean()`
**Chaînes de caractères :**
* **`prefer-template`** : Préfère les template literals aux concaténations
* **`no-useless-concat`** : Interdit les concaténations inutiles
**Comparaisons et égalités :**
* **`eqeqeq`** : Interdit `==` et `!=` (utiliser `===` et `!==`)
* **`yoda`** : Interdit les conditions Yoda (`if ("red" === color)`)
**Structures de contrôle :**
* **`curly`** : Accolades obligatoires même pour une seule ligne
* **`no-else-return`** : Interdit `else` après `return`
* **`no-lonely-if`** : Interdit `if` seul dans `else` (utiliser `else if`)
* **`no-nested-ternary`** : Interdit les ternaires imbriqués
* **`no-unneeded-ternary`** : Interdit les ternaires inutiles
* **`no-continue`** : Interdit `continue`
* **`no-labels`** : Interdit les labels
**Fonctions :**
* **`prefer-arrow-callback`** : Préfère les arrow functions
* **`prefer-rest-params`** : Préfère les rest parameters à `arguments`
* **`no-confusing-arrow`** : Interdit les arrow functions ambiguës
* **`no-param-reassign`** : Interdit la réassignation des paramètres (propriétés autorisées)
**Retours et assignations :**
* **`no-useless-return`** : Interdit les `return` inutiles
* **`no-return-assign`** : Interdit les assignations dans `return`
* **`no-multi-assign`** : Interdit les assignations multiples (`a = b = c`)
**Erreurs :**
* **`no-throw-literal`** : Interdit de lancer des primitives (utiliser `Error`)
**Autres :**
* **`no-implicit-coercion`** : Interdit la coercition implicite
* **`no-useless-constructor`** : Interdit les constructeurs inutiles
* **`no-useless-call`** : Interdit les appels inutiles
* **`no-useless-computed-key`** : Interdit les clés calculées inutiles
* **`no-useless-rename`** : Interdit les renommages inutiles
* **`no-whitespace-before-property`** : Interdit les espaces avant les propriétés
* **`no-sequences`** : Interdit les séquences d'expressions
* **`no-iterator`** : Interdit `__iterator__`
* **`no-proto`** : Interdit `__proto__`
* **`no-bitwise`** : Interdit les opérateurs bitwise
* **`no-multi-str`** : Interdit les chaînes multi-lignes
* **`no-new`** : Interdit `new` sans assignation
* **`spaced-comment`** : Commentaires doivent être espacés
**Syntaxe restreinte :**
* **`no-restricted-syntax`** : Interdit certaines syntaxes
* `ForInStatement` : Utiliser `Object.keys()`, `Object.values()`, `Object.entries()`
* `LabeledStatement` : Les labels sont interdits
* `WithStatement` : `with` est interdit en mode strict
#### Console et debug
* **`no-console`** : Avertit sur `console.log` (autorise `console.warn` et `console.error`)
* **`no-debugger`** : Interdit `debugger`
* **`no-alert`** : Interdit `alert()`
#### TypeScript - Types et sécurité
**Types explicites :**
* **`@typescript-eslint/no-explicit-any`** : Interdit `any`
* **`@typescript-eslint/explicit-module-boundary-types`** : Types explicites requis pour les exports de modules
**Sécurité des types :**
* **`@typescript-eslint/no-unsafe-assignment`** : Interdit les assignations non sûres
* **`@typescript-eslint/no-unsafe-member-access`** : Interdit les accès membres non sûrs
* **`@typescript-eslint/no-unsafe-call`** : Interdit les appels non sûrs
* **`@typescript-eslint/no-unsafe-return`** : Interdit les retours non sûrs
* **`@typescript-eslint/no-unsafe-argument`** : Interdit les arguments non sûrs
* **`@typescript-eslint/restrict-template-expressions`** : Restreint les expressions dans les template literals
* **`@typescript-eslint/restrict-plus-operands`** : Restreint les opérandes de l'opérateur `+`
**Qualité des types :**
* **`@typescript-eslint/no-redundant-type-constituents`** : Interdit les types redondants
* **`@typescript-eslint/prefer-reduce-type-parameter`** : Préfère les type parameters pour `reduce()`
**Méthodes préférées :**
* **`@typescript-eslint/prefer-includes`** : Préfère `includes()` à `indexOf() !== -1`
* **`@typescript-eslint/prefer-string-starts-ends-with`** : Préfère `startsWith()`/`endsWith()` aux regex
**Autres :**
* **`@typescript-eslint/no-require-imports`** : Désactivé (autorise `require()`)
* **`@typescript-eslint/no-shadow`** : Interdit l'ombre de variables (remplace `no-shadow`)
* **`@typescript-eslint/no-use-before-define`** : Interdit l'utilisation avant définition (fonctions autorisées, classes et variables interdites)
#### React - Qualité et performance
**Configuration React :**
* **`react/react-in-jsx-scope`** : Désactivé (React 17+)
* **`react/prop-types`** : Désactivé (TypeScript gère les types)
* **`react/jsx-uses-react`** : Désactivé (React 17+)
**Clés et props :**
* **`react/jsx-key`** : Clés obligatoires dans les listes
* **`react/jsx-no-duplicate-props`** : Interdit les props dupliquées
* **`react/jsx-no-undef`** : Interdit les variables non définies dans JSX
* **`react/jsx-uses-vars`** : Variables utilisées dans JSX doivent être déclarées
* **`react/no-array-index-key`** : Avertit sur l'utilisation de l'index comme key
**Children et props :**
* **`react/no-children-prop`** : Interdit `children` comme prop
* **`react/no-danger-with-children`** : Interdit `dangerouslySetInnerHTML` avec children
**API dépréciées :**
* **`react/no-deprecated`** : Interdit les API dépréciées
* **`react/no-direct-mutation-state`** : Interdit la mutation directe du state
* **`react/no-find-dom-node`** : Interdit `findDOMNode()`
* **`react/no-is-mounted`** : Interdit `isMounted()`
* **`react/no-render-return-value`** : Interdit l'utilisation de la valeur de retour de `render()`
* **`react/no-string-refs`** : Interdit les string refs
* **`react/require-render-return`** : Return obligatoire dans `render()`
**JSX :**
* **`react/no-unescaped-entities`** : Interdit les entités non échappées
* **`react/no-unknown-property`** : Interdit les propriétés inconnues
* **`react/self-closing-comp`** : Composants auto-fermants requis
* **`react/jsx-boolean-value`** : Interdit les valeurs booléennes explicites (`prop={true}` → `prop`)
* **`react/jsx-curly-brace-presence`** : Interdit les `{}` inutiles dans props et children
* **`react/jsx-fragments`** : Préfère `<>` à `<React.Fragment>`
* **`react/jsx-no-useless-fragment`** : Interdit les fragments inutiles
* **`react/jsx-pascal-case`** : Composants en PascalCase
**Performance :**
* **`react/no-unstable-nested-components`** : Interdit les composants imbriqués instables
* **`react-hooks/exhaustive-deps`** : Dépendances exhaustives requises dans les hooks
* **`react-hooks/refs`** : Désactivé
#### Sécurité et patterns dangereux
**Évaluation de code :**
* **`no-eval`** : Interdit `eval()`
* **`no-implied-eval`** : Interdit l'évaluation implicite (`setTimeout("code")`)
* **`no-new-func`** : Interdit `new Function()`
* **`no-script-url`** : Interdit les URLs `javascript:`
**Autres :**
* **`no-void`** : Désactivé (utilisé pour ignorer les promesses : `void promise`)
* **`no-with`** : Interdit `with` statement
* **`no-caller`** : Interdit `caller`
* **`no-extend-native`** : Interdit l'extension des objets natifs
* **`no-global-assign`** : Interdit l'assignation de variables globales
* **`no-implicit-globals`** : Interdit les globals implicites
* **`no-restricted-globals`** : Restreint certains globals (`event`, `fdescribe`)
* **`no-shadow-restricted-names`** : Interdit l'ombre sur les noms restreints
#### Qualité et maintenabilité
**Assignations et déclarations :**
* **`no-misleading-character-class`** : Désactivé
* **`no-redeclare`** : Interdit la redéclaration
* **`no-self-assign`** : Interdit l'auto-assignation
* **`no-self-compare`** : Interdit l'auto-comparaison
* **`no-shadow`** : Désactivé (remplacé par `@typescript-eslint/no-shadow`)
* **`no-undef-init`** : Interdit l'initialisation à `undefined`
* **`no-undefined`** : Désactivé (`undefined` est parfois nécessaire)
**Utilisation avant définition :**
* **`no-use-before-define`** : Désactivé (remplacé par `@typescript-eslint/no-use-before-define`)
**Autres :**
* **`no-useless-call`** : Interdit les appels inutiles
* **`no-useless-computed-key`** : Interdit les clés calculées inutiles
* **`no-useless-rename`** : Interdit les renommages inutiles
* **`no-whitespace-before-property`** : Interdit les espaces avant les propriétés
* **`no-octal-escape`** : Interdit l'échappement octal
* **`spaced-comment`** : Commentaires doivent être espacés
### Analyse préalable obligatoire et arbre des fichiers ### Analyse préalable obligatoire et arbre des fichiers
Avant toute implémentation, une phase danalyse est obligatoire et doit produire une représentation de larbre des fichiers pertinents. Avant toute implémentation, une phase danalyse est obligatoire et doit produire une représentation de larbre des fichiers pertinents.
@ -331,7 +596,7 @@ Le projet est open source et hébergé sur Gitea auto-hébergé. Toutes les cont
### Repository et infrastructure ### Repository et infrastructure
* **Repository Gitea** : https://git.4nkweb.com/4nk/story-research-zapwall * **Repository Gitea** : <https://git.4nkweb.com/4nk/story-research-zapwall>
* **Templates** : Utiliser les templates d'issues et de PR dans `.gitea/` * **Templates** : Utiliser les templates d'issues et de PR dans `.gitea/`
* **Labels et organisation** : Utiliser les labels appropriés pour organiser les issues et PRs * **Labels et organisation** : Utiliser les labels appropriés pour organiser les issues et PRs
* **Branches** : Respecter la convention de nommage des branches (feature/, fix/, etc.) * **Branches** : Respecter la convention de nommage des branches (feature/, fix/, etc.)
@ -351,7 +616,7 @@ Le projet est open source et hébergé sur Gitea auto-hébergé. Toutes les cont
Tous les commits doivent suivre ce format structuré : Tous les commits doivent suivre ce format structuré :
``` ```text
Titre court et descriptif Titre court et descriptif
**Motivations:** **Motivations:**
@ -373,18 +638,18 @@ Titre court et descriptif
### Processus de commit ### Processus de commit
1. **Avant chaque commit** : 1. **Avant chaque commit** :
- Vérifier que le code compile (`npm run type-check`) * Vérifier que le code compile (`npm run type-check`)
- Vérifier le linting (`npm run lint`) * Vérifier le linting (`npm run lint`)
- Vérifier que les modifications sont complètes et fonctionnelles * Vérifier que les modifications sont complètes et fonctionnelles
2. **Création du commit** : 2. **Création du commit** :
- Utiliser `git add` pour les fichiers modifiés * Utiliser `git add` pour les fichiers modifiés
- Créer un commit avec le format structuré * Créer un commit avec le format structuré
- Ne pas utiliser `--no-verify` sauf cas exceptionnel documenté * Ne pas utiliser `--no-verify` sauf cas exceptionnel documenté
3. **Après le commit** : 3. **Après le commit** :
- Vérifier que le commit a bien été créé (`git log`) * Vérifier que le commit a bien été créé (`git log`)
- Documenter dans `fixKnowledge/` ou `features/` si nécessaire * Documenter dans `fixKnowledge/` ou `features/` si nécessaire
### Exceptions ### Exceptions

View File

@ -82,7 +82,7 @@ export async function getPurchaseById(purchaseId: string, timeoutMs: number = 50
resolve(value) resolve(value)
} }
sub.on('event', async (event: Event) => { sub.on('event', async (event: Event): Promise<void> => {
const parsed = await parsePurchaseFromEvent(event) const parsed = await parsePurchaseFromEvent(event)
if (parsed?.id === purchaseId) { if (parsed?.id === purchaseId) {
// Cache the parsed purchase // Cache the parsed purchase

View File

@ -80,7 +80,7 @@ export async function getReviewTipById(reviewTipId: string, timeoutMs: number =
resolve(value) resolve(value)
} }
sub.on('event', async (event: Event) => { sub.on('event', async (event: Event): Promise<void> => {
const parsed = await parseReviewTipFromEvent(event) const parsed = await parseReviewTipFromEvent(event)
if (parsed?.id === reviewTipId) { if (parsed?.id === reviewTipId) {
// Cache the parsed review tip // Cache the parsed review tip
@ -113,7 +113,7 @@ export function getReviewTipsForArticle(articleId: string, timeoutMs: number = 5
const sub = createSubscription(pool, [relayUrl], filters) const sub = createSubscription(pool, [relayUrl], filters)
let finished = false let finished = false
const done = async () => { const done = async (): Promise<void> => {
if (finished) { if (finished) {
return return
} }
@ -122,7 +122,7 @@ export function getReviewTipsForArticle(articleId: string, timeoutMs: number = 5
resolve(results) resolve(results)
} }
sub.on('event', async (event: Event) => { sub.on('event', async (event: Event): Promise<void> => {
const parsed = await parseReviewTipFromEvent(event) const parsed = await parseReviewTipFromEvent(event)
if (parsed?.articleId === articleId) { if (parsed?.articleId === articleId) {
// Cache the parsed review tip // Cache the parsed review tip
@ -133,7 +133,9 @@ export function getReviewTipsForArticle(articleId: string, timeoutMs: number = 5
} }
}) })
sub.on('eose', () => done()) sub.on('eose', (): void => {
void done()
})
setTimeout(() => done(), timeoutMs).unref?.() setTimeout(() => done(), timeoutMs).unref?.()
}) })
} }
@ -153,7 +155,7 @@ export function getReviewTipsForReview(reviewId: string, timeoutMs: number = 500
const sub = createSubscription(pool, [relayUrl], filters) const sub = createSubscription(pool, [relayUrl], filters)
let finished = false let finished = false
const done = async () => { const done = async (): Promise<void> => {
if (finished) { if (finished) {
return return
} }
@ -162,7 +164,7 @@ export function getReviewTipsForReview(reviewId: string, timeoutMs: number = 500
resolve(results) resolve(results)
} }
sub.on('event', async (event: Event) => { sub.on('event', async (event: Event): Promise<void> => {
const parsed = await parseReviewTipFromEvent(event) const parsed = await parseReviewTipFromEvent(event)
if (parsed?.reviewId === reviewId) { if (parsed?.reviewId === reviewId) {
// Cache the parsed review tip // Cache the parsed review tip
@ -173,7 +175,9 @@ export function getReviewTipsForReview(reviewId: string, timeoutMs: number = 500
} }
}) })
sub.on('eose', () => done()) sub.on('eose', (): void => {
void done()
})
setTimeout(() => done(), timeoutMs).unref?.() setTimeout(() => done(), timeoutMs).unref?.()
}) })
} }