
zero-warnings-enforcer
by RomualdP
Volley App Monorepo
SKILL.md
name: Zero Warnings Enforcer description: Applique la philosophie "Broken Window" avec zéro tolérance pour les warnings/errors ESLint et TypeScript. JAMAIS désactiver les règles, TOUJOURS corriger la cause racine. À utiliser lors de la correction d'erreurs, warnings, ou quand l'utilisateur mentionne "eslint", "typescript error", "warning", "linting", "quality", "broken window", "@ts-ignore", "eslint-disable". allowed-tools: [Read, Write, Edit, Glob, Grep, Bash]
Zero Warnings Enforcer
🎯 Mission
Maintenir une qualité de code irréprochable en appliquant la philosophie Broken Window : ZÉRO warning, ZÉRO error, JAMAIS de désactivation de règles.
🪟 Philosophie : Broken Window Theory
Le Principe
Broken Window Theory (Théorie de la vitre brisée) :
- Une vitre cassée non réparée → Signal d'abandon
- Autres vitres brisées → Dégradation rapide
- Bâtiment entier dégradé en quelques semaines
Application au code :
- Un warning ignoré → Signal que la qualité n'est pas prioritaire
- Autres warnings ajoutés → Accumulation de dette technique
- Code base dégradée → Maintenabilité impossible
La Règle d'Or
Un projet de qualité a ZÉRO warning, ZÉRO error, ZÉRO désactivation de règle.
Conséquences :
- ✅ Code maintenable à long terme
- ✅ Onboarding facile (pas de "legacy code" sale)
- ✅ Confiance dans le code (pas de surprises)
- ✅ Refactoring sécurisé (typage strict)
- ✅ Bugs détectés tôt (ESLint + TypeScript)
🚫 Règles INTERDITES
JAMAIS Désactiver ESLint
// ❌ INTERDIT - ABSOLUMENT JAMAIS
/* eslint-disable */
const badCode = () => { ... };
// ❌ INTERDIT - ABSOLUMENT JAMAIS
// eslint-disable-next-line no-unused-vars
const unusedVariable = 'bad';
// ❌ INTERDIT - ABSOLUMENT JAMAIS
/* eslint-disable-next-line */
const veryBadCode = () => { ... };
Pourquoi c'est INTERDIT :
- Cache un problème au lieu de le résoudre
- Crée de la dette technique
- Signal de code de mauvaise qualité
- Accumulation rapide de désactivations
Exception RARE (documentée) :
// ✅ ACCEPTABLE UNIQUEMENT SI :
// 1. Raison documentée
// 2. Approuvée en code review
// 3. Temporaire avec TODO et date limite
// 4. Aucune alternative possible
/* eslint-disable-next-line no-console */
console.log('Debug log - TODO: Remove before 2024-12-31'); // Ticket JIRA-123
JAMAIS Ignorer TypeScript
// ❌ INTERDIT - ABSOLUMENT JAMAIS
// @ts-ignore
const badType: string = 123;
// ❌ INTERDIT - ABSOLUMENT JAMAIS
// @ts-expect-error
const alsoBAD = someFunction();
// ❌ INTERDIT - ABSOLUMENT JAMAIS
const lazyTyping: any = { foo: 'bar' };
// ❌ INTERDIT - ABSOLUMENT JAMAIS
const unknownStuff: unknown = getData();
Pourquoi c'est INTERDIT :
- Perd le bénéfice du typage statique
- Bugs en production (erreurs de type non détectées)
- Perte de l'autocomplétion
- Refactoring dangereux
JAMAIS de Types Faibles
// ❌ INTERDIT
const data: any = fetchData();
// ❌ INTERDIT
const result: unknown = processData();
// ❌ INTERDIT
function process(input: any): any {
return input;
}
// ✅ CORRECT
interface UserData {
id: string;
name: string;
email: string;
}
const data: UserData = fetchData();
const result: ProcessedData = processData();
function process(input: UserInput): UserOutput {
return transform(input);
}
🔧 ESLint Errors - Comment les Corriger
1. no-unused-vars - Variables Non Utilisées
// ❌ MAUVAIS - Génère un warning
const unusedVariable = 'value';
function Component({ unusedProp, usedProp }: Props) {
return <div>{usedProp}</div>;
}
// ✅ CORRECT - Supprimer la variable
// (Si vraiment inutile)
// ✅ CORRECT - Utiliser la variable
const usedVariable = 'value';
console.log(usedVariable);
// ✅ CORRECT - Préfixer avec _ si intentionnel (destructuring)
function Component({ _unusedProp, usedProp }: Props) {
return <div>{usedProp}</div>;
}
2. no-console - Console Logs
// ❌ MAUVAIS - console.log en production
console.log('Debug data:', data);
console.error('Error:', error);
// ✅ CORRECT - Utiliser un logger
import { logger } from '@/lib/logger';
logger.debug('Debug data:', data);
logger.error('Error:', error);
// ✅ CORRECT - Créer un logger custom
class Logger {
debug(message: string, data?: any) {
if (process.env.NODE_ENV === 'development') {
console.log(`[DEBUG] ${message}`, data);
}
}
error(message: string, error?: Error) {
// Log to external service (Sentry, etc.)
console.error(`[ERROR] ${message}`, error);
}
}
export const logger = new Logger();
3. @typescript-eslint/no-explicit-any - Type any
// ❌ MAUVAIS
function process(data: any) {
return data.value;
}
// ✅ CORRECT - Type explicite
interface InputData {
value: string;
}
function process(data: InputData): string {
return data.value;
}
// ✅ CORRECT - Generic si type inconnu
function process<T extends { value: string }>(data: T): string {
return data.value;
}
// ✅ CORRECT - Union type si plusieurs types possibles
function process(data: string | number | boolean): string {
return String(data);
}
4. react-hooks/exhaustive-deps - useEffect Dependencies
// ❌ MAUVAIS - Missing dependency
useEffect(() => {
fetchData(userId); // userId is missing from deps
}, []);
// ❌ MAUVAIS - Désactiver la règle
useEffect(() => {
fetchData(userId);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// ✅ CORRECT - Ajouter la dépendance
useEffect(() => {
fetchData(userId);
}, [userId]); // Correct!
// ✅ CORRECT - Extraire primitive si object
useEffect(() => {
fetchData(user.id);
}, [user.id]); // Primitive, stable
// ✅ CORRECT - useCallback pour fonctions
const fetchData = useCallback((id: string) => {
// fetch logic
}, []);
useEffect(() => {
fetchData(userId);
}, [userId, fetchData]); // Stable function
5. @typescript-eslint/no-unused-vars - Imports Non Utilisés
// ❌ MAUVAIS - Import non utilisé
import { useState, useEffect, useMemo } from 'react'; // useMemo unused
// ✅ CORRECT - Supprimer l'import
import { useState, useEffect } from 'react';
// ✅ CORRECT - Utiliser l'import
import { useState, useEffect, useMemo } from 'react';
const memoized = useMemo(() => compute(), []);
6. @typescript-eslint/no-non-null-assertion - Non-null Assertion
// ❌ MAUVAIS - Non-null assertion
const value = data!.value;
const user = users.find(u => u.id === id)!;
// ✅ CORRECT - Vérifier null
const value = data?.value;
if (!data) {
throw new Error('Data is required');
}
const value = data.value;
// ✅ CORRECT - Type guard
function isData(data: Data | null): data is Data {
return data !== null;
}
if (isData(data)) {
const value = data.value; // Type safe
}
// ✅ CORRECT - Optional chaining + fallback
const value = data?.value ?? 'default';
7. @typescript-eslint/no-floating-promises - Promises Non Gérées
// ❌ MAUVAIS - Promise non await
fetchData(); // Floating promise
async function handler() {
saveData(); // Promise non await
}
// ✅ CORRECT - Await la promise
await fetchData();
async function handler() {
await saveData();
}
// ✅ CORRECT - void si intentionnel (fire and forget)
void fetchData(); // Explicit fire-and-forget
// ✅ CORRECT - .catch() si fire-and-forget
fetchData().catch(error => {
logger.error('Background fetch failed', error);
});
🔷 TypeScript Errors - Comment les Corriger
1. Type 'X' is not assignable to type 'Y'
// ❌ MAUVAIS
const id: string = 123; // Error
// ❌ MAUVAIS - @ts-ignore
// @ts-ignore
const id: string = 123;
// ✅ CORRECT - Convertir le type
const id: string = String(123);
// ✅ CORRECT - Changer le type
const id: number = 123;
// ✅ CORRECT - Union type si plusieurs types
const id: string | number = 123;
2. Object is possibly 'null' or 'undefined'
// ❌ MAUVAIS
const name = user.name; // user peut être null
// ❌ MAUVAIS - Non-null assertion
const name = user!.name;
// ✅ CORRECT - Optional chaining
const name = user?.name;
// ✅ CORRECT - Type guard
if (!user) {
throw new Error('User is required');
}
const name = user.name; // Type safe
// ✅ CORRECT - Nullish coalescing
const name = user?.name ?? 'Anonymous';
3. Property 'X' does not exist on type 'Y'
// ❌ MAUVAIS
const value = obj.unknownProperty; // Property doesn't exist
// ❌ MAUVAIS - any
const obj: any = getData();
const value = obj.unknownProperty;
// ✅ CORRECT - Définir le type correct
interface MyObject {
knownProperty: string;
unknownProperty: string; // Add missing property
}
const obj: MyObject = getData();
const value = obj.unknownProperty;
// ✅ CORRECT - Type guard
function hasProperty<T, K extends string>(
obj: T,
key: K
): obj is T & Record<K, unknown> {
return key in obj;
}
if (hasProperty(obj, 'unknownProperty')) {
const value = obj.unknownProperty; // Type safe
}
4. Argument of type 'X' is not assignable to parameter of type 'Y'
// ❌ MAUVAIS
function greet(name: string) {
console.log(`Hello ${name}`);
}
greet(123); // Error
// ❌ MAUVAIS - any
greet(123 as any);
// ✅ CORRECT - Convertir le type
greet(String(123));
// ✅ CORRECT - Générique si fonction accepte plusieurs types
function greet<T extends string | number>(name: T) {
console.log(`Hello ${name}`);
}
greet(123); // OK
greet('John'); // OK
5. Cannot find name 'X'
// ❌ MAUVAIS
const result = unknownFunction(); // Function not imported
// ✅ CORRECT - Importer la fonction
import { unknownFunction } from '@/lib/utils';
const result = unknownFunction();
// ✅ CORRECT - Définir la fonction si manquante
function unknownFunction(): string {
return 'result';
}
const result = unknownFunction();
6. Index signature is missing in type 'X'
// ❌ MAUVAIS
const obj: { name: string } = { name: 'John' };
const key = 'age';
const value = obj[key]; // Error
// ❌ MAUVAIS - any
const obj: any = { name: 'John' };
// ✅ CORRECT - Index signature
interface User {
name: string;
[key: string]: string; // Index signature
}
const obj: User = { name: 'John' };
const value = obj[key]; // OK
// ✅ CORRECT - Type guard
function isValidKey<T extends object>(
obj: T,
key: string | number | symbol
): key is keyof T {
return key in obj;
}
if (isValidKey(obj, key)) {
const value = obj[key]; // Type safe
}
// ✅ CORRECT - Record type
const obj: Record<string, string> = { name: 'John' };
const value = obj[key]; // OK
✅ Quality Gates
1. Pre-commit Hooks (Husky + lint-staged)
// package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,tsx}": [
"eslint --max-warnings 0", // ZERO warnings tolerated
"tsc --noEmit" // Type check
]
}
}
2. CI/CD Pipeline (Strict Mode)
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: yarn install
# ESLint - ZERO warnings
- name: ESLint (Zero Warnings)
run: yarn lint --max-warnings 0
# TypeScript - Strict mode
- name: TypeScript Check
run: yarn tsc --noEmit
# Tests - MUST pass
- name: Run Tests
run: yarn test
# Build - MUST succeed
- name: Build
run: yarn build
3. ESLint Configuration (Strict)
// .eslintrc.js
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
rules: {
// STRICT RULES
'@typescript-eslint/no-explicit-any': 'error', // any = ERROR
'@typescript-eslint/no-unused-vars': 'error', // unused = ERROR
'no-console': 'error', // console.log = ERROR
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/strict-boolean-expressions': 'error',
// NO WARNINGS ALLOWED
'max-warnings': 0,
},
};
4. TypeScript Configuration (Strict)
// tsconfig.json
{
"compilerOptions": {
"strict": true, // Enable ALL strict checks
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
📋 Checklist Qualité (Avant Commit)
✅ Avant Chaque Commit
-
yarn lint --max-warnings 0passe sans erreur -
yarn tsc --noEmitpasse sans erreur -
yarn testpasse tous les tests -
yarn buildréussit sans warnings - Aucun
eslint-disableajouté - Aucun
@ts-ignoreajouté - Aucun
anyouunknownajouté - Aucun
console.loglaissé (utiliser logger) - Code review personnel effectuée
✅ Avant Chaque PR
- Tous les commits respectent la checklist
- CI/CD passe (0 warning, 0 error)
- Code coverage maintenu ou amélioré
- Documentation à jour
- Tests ajoutés pour nouvelles features
🚨 Exceptions RARES (Documentées)
Quand Désactiver une Règle EST Acceptable
Cas TRÈS RARES (< 1% du code) :
- Bug ESLint/TypeScript : Règle buggée connue
- Code généré : Fichiers auto-générés (Prisma client, etc.)
- Migration legacy : Code legacy avec date limite de migration
Template d'exception :
// EXCEPTION DOCUMENTÉE
// Raison: [Explication détaillée]
// Ticket: JIRA-123
// Deadline: 2024-12-31
// Reviewer: @john-doe
/* eslint-disable-next-line no-console */
console.log('Legacy debug - Migration en cours');
Règles pour exceptions :
- ✅ TOUJOURS documenter la raison
- ✅ TOUJOURS créer un ticket de suivi
- ✅ TOUJOURS fixer une deadline
- ✅ TOUJOURS faire valider en code review
- ✅ TOUJOURS scope minimal (
-next-line, pasdisable)
🎓 Exemples Complets
Exemple 1 : Corriger un useEffect avec Dependencies
// ❌ AVANT - Warning exhaustive-deps
function Component({ userId }: Props) {
const [data, setData] = useState(null);
useEffect(() => {
fetchData(userId).then(setData);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // Missing userId dependency
return <div>{data?.name}</div>;
}
// ✅ APRÈS - Correction complète
function Component({ userId }: Props) {
const [data, setData] = useState<UserData | null>(null);
useEffect(() => {
let cancelled = false;
async function loadData() {
const result = await fetchData(userId);
if (!cancelled) {
setData(result);
}
}
void loadData();
return () => {
cancelled = true; // Cleanup
};
}, [userId]); // Correct dependency
return <div>{data?.name}</div>;
}
Exemple 2 : Corriger un Type any
// ❌ AVANT - Type any
function processData(data: any) {
return {
id: data.id,
name: data.name,
items: data.items.map((item: any) => item.value),
};
}
// ✅ APRÈS - Types explicites
interface InputData {
id: string;
name: string;
items: Array<{ value: string }>;
}
interface ProcessedData {
id: string;
name: string;
items: string[];
}
function processData(data: InputData): ProcessedData {
return {
id: data.id,
name: data.name,
items: data.items.map(item => item.value),
};
}
📚 Skills Complémentaires
Pour aller plus loin :
- refactoring : Refactoring best practices
- testing : Testing standards pour prévenir les régressions
- code-reviewer : Code review checklist
Rappel : Un warning = Une fenêtre cassée = Début de la dégradation. Corrigez IMMÉDIATEMENT, ne JAMAIS ignorer.
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon