Prettier ile Otomatik Kod Formatlama ve ESLint Entegrasyonu

Kod tabanı büyüdükçe, ekip içinde herkesin farklı format tercihleri olduğu bir kaos ortamı kaçınılmaz hale gelir. Bir geliştirici 2 boşluk kullanırken diğeri 4 boşluk, birisi tek tırnak tercih ederken diğeri çift tırnak. Bu durum PR review’larını gereksiz yere uzatır, commit geçmişini kirletir ve ekip moralini düşürür. Prettier ve ESLint ikilisi bu sorunun kalıcı çözümü. Ama ikisini birlikte doğru kurmak, ilk bakışta göründüğü kadar basit değil.

Temel Fark: Prettier Ne Yapar, ESLint Ne Yapar?

Bu iki araç sık sık karıştırılıyor. Net bir ayrım yapmak gerekirse:

Prettier tamamen bir kod formatlayıcıdır. Noktalı virgül, tırnak tipi, satır uzunluğu, girintileme gibi görsel düzenlemelerle ilgilenir. Kodun ne yaptığıyla değil, nasıl göründüğüyle ilgilenir.

ESLint ise bir statik analiz aracıdır. Kullanılmayan değişkenler, tanımsız referanslar, güvensiz pratikler gibi kod kalitesi sorunlarını tespit eder. Bir kısmı formatla ilgili kurallar içerse de asıl amacı kod doğruluğu ve güvenliğidir.

İkisini birlikte kullanmak mantıklı, ama çakışan kuralları iyi yönetmezseniz sürekli birbiriyle savaşan iki araç elde edersiniz. Bunu yaşadım, çok can sıkıcı.

Kurulum: Temiz Bir Başlangıç

Node.js projeniz olduğunu varsayarak ilerliyorum. Önce bağımlılıkları kuralım:

npm install --save-dev prettier eslint eslint-config-prettier eslint-plugin-prettier

Eğer TypeScript kullanıyorsanız bunları da ekleyin:

npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin

eslint-config-prettier burada kritik bir rol oynar. ESLint’in Prettier ile çakışan kurallarını devre dışı bırakır. eslint-plugin-prettier ise Prettier’ı ESLint kuralı olarak çalıştırır, böylece ESLint çıktısında format hatalarını da görürsünüz.

Prettier Konfigürasyonu

Proje kökünde .prettierrc dosyası oluşturun:

cat > .prettierrc << 'EOF'
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf"
}
EOF

Bu ayarları kısaca açıklayayım:

  • semi: Satır sonlarına noktalı virgül ekler
  • trailingComma: ES5’te geçerli yerlere trailing comma ekler (git diff’leri temizler)
  • singleQuote: String’lerde tek tırnak kullanır
  • printWidth: Satır uzunluğu sınırı, 100 karakter makul bir değer
  • tabWidth: Girinti genişliği
  • useTabs: Tab yerine boşluk kullan
  • bracketSpacing: Nesne literallerinde parantez içi boşluk bırakır
  • arrowParens: Arrow function parametrelerini her zaman paranteze alır
  • endOfLine: Satır sonu karakteri, cross-platform tutarlılık için lf tercih edin

.prettierignore dosyasını da unutmayın:

cat > .prettierignore << 'EOF'
node_modules/
dist/
build/
coverage/
*.min.js
*.min.css
package-lock.json
yarn.lock
EOF

ESLint Konfigürasyonu

ESLint konfigürasyonunu oluştururken Prettier entegrasyonunu baştan dahil etmek gerekiyor. .eslintrc.json dosyası:

cat > .eslintrc.json << 'EOF'
{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "prettier"
  ],
  "plugins": [
    "prettier"
  ],
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "rules": {
    "prettier/prettier": "error",
    "no-unused-vars": "warn",
    "no-console": "warn",
    "eqeqeq": "error",
    "no-var": "error",
    "prefer-const": "error",
    "no-duplicate-imports": "error"
  }
}
EOF

Burada "extends" dizisinde "prettier"‘ın en sona gelmesi şart. Bu sıralama, Prettier’ın çakışan ESLint kurallarını override etmesini sağlar.

TypeScript projesi için konfigürasyon biraz farklı olacak:

cat > .eslintrc.json << 'EOF'
{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "@typescript-eslint/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "@typescript-eslint",
    "prettier"
  ],
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module",
    "project": "./tsconfig.json"
  },
  "rules": {
    "prettier/prettier": "error",
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": "warn",
    "@typescript-eslint/explicit-function-return-type": "warn",
    "@typescript-eslint/no-explicit-any": "warn",
    "no-console": "warn",
    "eqeqeq": "error",
    "no-var": "error",
    "prefer-const": "error"
  }
}
EOF

package.json Script’leri

Günlük kullanım için pratik script’ler tanımlayalım:

# package.json içine eklenecek scripts bloğu
cat >> /tmp/scripts-snippet.json << 'EOF'
{
  "scripts": {
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
    "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
    "format": "prettier --write "src/**/*.{js,jsx,ts,tsx,css,json,md}"",
    "format:check": "prettier --check "src/**/*.{js,jsx,ts,tsx,css,json,md}"",
    "lint:all": "npm run lint && npm run format:check"
  }
}
EOF

format:check komutu CI/CD pipeline’ında kullanmak için idealdir; dosyaları değiştirmez, sadece kontrol eder ve uyumsuzluk varsa non-zero exit code döner.

VS Code ile Entegrasyon

Editör entegrasyonu kurulmadan bu araçlar potansiyelinin yarısını kullanamaz. VS Code için .vscode/settings.json dosyası:

mkdir -p .vscode
cat > .vscode/settings.json << 'EOF'
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}
EOF

Bu ayarlarla dosya kaydedildiğinde hem Prettier formatlaması hem de ESLint auto-fix devreye girer. Gerekli VS Code extension’ları:

  • esbenp.prettier-vscode: Prettier extension
  • dbaeumer.vscode-eslint: ESLint extension

Bu extension’ları proje bazında önerilen extension olarak da ekleyebilirsiniz:

cat > .vscode/extensions.json << 'EOF'
{
  "recommendations": [
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint"
  ]
}
EOF

Git Hook’ları ile Otomatik Kontrol: Husky ve lint-staged

Kod CI’a gitmeden önce yerel makinede kontrol etmek hem daha hızlı geri bildirim verir hem de CI kaynaklarından tasarruf sağlar. Husky ve lint-staged bu iş için biçilmiş kaftan.

npm install --save-dev husky lint-staged
npx husky install
npm pkg set scripts.prepare="husky install"
npx husky add .husky/pre-commit "npx lint-staged"

package.json‘a lint-staged konfigürasyonu ekleyin:

# package.json içine eklenecek lint-staged bloğu
cat >> /tmp/lint-staged-snippet.json << 'EOF'
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "prettier --write",
      "eslint --fix",
      "eslint"
    ],
    "*.{css,scss,json,md,yaml,yml}": [
      "prettier --write"
    ]
  }
}
EOF

Bu yapılandırmayla commit öncesinde sadece staged dosyalar kontrol edilir, tüm projeyi taramak yerine. Bu önemli bir performans farkı yaratır büyük projelerde.

Eğer ESLint hatası commit’i engelliyorsa ve acil bir durumdasanız:

git commit -m "acil fix" --no-verify

Ama bunu alışkanlık haline getirmeyin. --no-verify bir kaçış kapısı, kurallara karşı gelmenin aracı değil.

CI/CD Pipeline Entegrasyonu

GitHub Actions ile nasıl entegre edebileceğinizi göstereyim:

cat > .github/workflows/code-quality.yml << 'EOF'
name: Code Quality

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  lint-and-format:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Check Prettier formatting
        run: npm run format:check
      
      - name: Run ESLint
        run: npm run lint
      
      - name: ESLint report
        if: failure()
        run: npm run lint -- --format=json --output-file=eslint-report.json || true
      
      - name: Upload ESLint report
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: eslint-report
          path: eslint-report.json
EOF

Bu pipeline, PR açıldığında ve main/develop’a push yapıldığında otomatik çalışır. Hata durumunda ESLint raporunu artifact olarak yükler, incelemek kolaylaşır.

Gerçek Dünya Senaryosu: Mevcut Projeyi Migrate Etmek

Yeni projelerde sıfırdan kurmak nispeten kolay. Asıl zorluk, yüzlerce dosyası olan ve hiç format standardı olmayan mevcut bir projeyi migrate etmek.

Böyle bir durumda izlediğim yaklaşım şu:

Önce tek bir “format fix” commit’i atın. Bu commit’i ayrı tutmak önemli, çünkü git blame karmaşası yaşanmasın:

# Önce tüm projeyi formatla
npx prettier --write "src/**/*.{js,jsx,ts,tsx}"

# ESLint auto-fix çalıştır
npx eslint src --ext .js,.jsx,.ts,.tsx --fix

# Kalan hataları görüntüle
npx eslint src --ext .js,.jsx,.ts,.tsx

# Değişiklikleri commit'le, sadece format değişikliklerini
git add -A
git commit -m "chore: apply prettier and eslint formatting to existing codebase"

Bu commit’ten sonra husky ve lint-staged’i aktif edin. Böylece git history’de “format tsunami”yi tek bir commit’e izole etmiş olursunuz ve bundan sonraki commit’ler temiz.

Büyük projelerde ESLint’in onlarca hatası olabilir ve hepsini bir anda düzeltmek mümkün olmayabilir. Bu durumda --max-warnings flag’ini kullanarak mevcut uyarı sayısını bir tavan olarak belirleyebilirsiniz:

# Mevcut uyarı sayısını öğren
npx eslint src --ext .js,.ts | tail -1

# CI'da bu sayıyı tavan olarak kullan, zamanla azaltın
npx eslint src --ext .js,.ts --max-warnings=47

Her sprint bu sayıyı biraz düşürün. Teknik borcu kademeli olarak ödemenin pratik bir yolu.

ESLint Rule Disable Yorumları: Ne Zaman, Nasıl?

Bazen bir kuralı belirli bir satır için devre dışı bırakmak gerekir. Bunun doğru kullanımı var, kötüye kullanımı var.

Satır bazında disable:

# Bu örnekler gerçek JavaScript kodu olduğundan bash bloğu yerine gösterim amaçlıdır
# eslint-disable-next-line no-console
console.log('Bu log production loggerımız henüz entegre edilmedi');

# Tek satır inline
const data = response.data; // eslint-disable-line @typescript-eslint/no-explicit-any

Blok bazında disable:

# eslint-disable no-unused-vars
const legacyVariable = getLegacyData();
const anotherLegacy = processLegacy(legacyVariable);
# eslint-enable no-unused-vars

Bir kuralı sürekli disable ediyorsanız bu genellikle iki şeyden birini işaret eder: ya kural proje için uygun değil ve .eslintrc‘den kaldırılmalı, ya da kod tasarımında bir sorun var ve düzeltilmeli. Disable yorumlarını “sonra düzeltilecek” etiketi olarak değil, gerçekten istisna olan durumlar için kullanın.

Monorepo Yapılarında Konfigürasyon

Birden fazla paketi olan monorepo yapılarında konfigürasyonu yönetmek farklılaşır. Kök dizinde paylaşılan bir temel konfigürasyon, her pakette override:

# packages/shared-config/.eslintrc.base.json (temel konfigürasyon)
cat > packages/shared-config/.eslintrc.base.json << 'EOF'
{
  "extends": ["eslint:recommended", "prettier"],
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": "error",
    "no-unused-vars": "warn",
    "prefer-const": "error"
  }
}
EOF

# packages/frontend/.eslintrc.json (frontend paketi için override)
cat > packages/frontend/.eslintrc.json << 'EOF'
{
  "extends": ["../../packages/shared-config/.eslintrc.base.json"],
  "env": {
    "browser": true
  },
  "rules": {
    "no-console": "error"
  }
}
EOF

# packages/backend/.eslintrc.json (backend paketi için override)
cat > packages/backend/.eslintrc.json << 'EOF'
{
  "extends": ["../../packages/shared-config/.eslintrc.base.json"],
  "env": {
    "node": true
  },
  "rules": {
    "no-console": "off"
  }
}
EOF

Prettier konfigürasyonunu monorepo kökünde tutmak ve tüm paketlerin ortak .prettierrc‘yi kullanmasını sağlamak genellikle daha pratiktir.

Yaygın Sorunlar ve Çözümleri

Kurulum sürecinde sıklıkla karşılaşılan problemler:

Prettier ve ESLint çakışması: eslint-config-prettier kurulu ve extends dizisinde en sonda olduğundan emin olun. Hangi kuralların devre dışı bırakıldığını görmek için npx eslint-config-prettier src/index.js çalıştırın.

Windows ve Unix satır sonu farkı: .prettierrc‘de "endOfLine": "lf" ayarını ve .gitattributes dosyasında * text=auto eol=lf satırını ekleyin. Aksi halde Windows’ta geliştirme yapan ekip üyeleriyle sürekli satır sonu çakışması yaşarsınız.

Husky pre-commit hook çalışmıyor: chmod +x .husky/pre-commit çalıştırın. Özellikle farklı işletim sistemleri arasında geçişte izin sorunu yaşanabiliyor.

lint-staged çok yavaş: --cache flag’ini ekleyin: "eslint --fix --cache". ESLint sonuçlarını cache’leyerek sadece değişen dosyaları yeniden analiz eder.

Prettier belirli dosyaları formatlamaması: .prettierignore dosyasını kontrol edin ve npx prettier --check dosya.js --debug-check ile debug yapın.

Sonuç

Prettier ve ESLint’i birlikte doğru konfigüre etmek, başlangıçta birkaç saatlik yatırım gerektiriyor. Ama bu yatırımın geri dönüşü çok net: PR review’larında format tartışmaları sona erer, yeni geliştirici onboarding’i kolaylaşır, kod tabanı tutarlı kalır.

Özellikle vurgulamak istediğim nokta şu: araçları kurmak yeterli değil, ekibin bu araçların neden var olduğunu anlaması gerekiyor. “Neden benim güzel kodumu bu araç değiştiriyor?” sorusuna cevap verebilmek önemli. Cevap basit aslında: bireysel tercihlerin değil, takım tutarlılığının öncelikli olduğu bir ortam yaratmak için.

CI pipeline’a entegre ederek bu kuralları zorunlu hale getirin. İsteğe bağlı bırakılan araçlar er geç kullanılmaz hale gelir. Otomasyon olmadan sürdürülebilir kod kalitesi sağlamak çok zor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir