Linting-Stack für Node-Projekte: Code-Qualität, Formatierung und Prosa-Linting

Seite 2: Integration mit Husky Pre-Commit Hooks

Inhaltsverzeichnis

Sind alle Linter konfiguriert, mĂĽssen diese jeweils einzeln aufgerufen werden. Entwicklerinnen und Entwickler sollten nicht vergessen, die Linting-Schritte auch auszufĂĽhren:

npm run lint:check
npm run format:check
npm run prose

Diese Arbeit kann alternativ auch ein Pre-Commit-Hook erledigen. Im Falle eines Node-Projektes steht dafür das Tool Husky parat. Es ermöglicht, alle drei Linting-Stufen automatisch vor jedem Commit auszuführen. Die Installation erfolgt über:

npm install -D husky
npx husky install

Das prepare-Skript in der Datei package.json stellt sicher, dass Husky nach der Installation mit npm install automatisch konfiguriert wird:

{
  "scripts": {
    "prepare": "husky install"
  }
}

Nach der Installation steht das Verzeichnis .husky im Root-Verzeichnis des Repository zur Verfügung. Darin findet sich ein Unterverzeichnis, das als Namen nur den Unterstrich _ trägt. Seit Version 8 verwendet Husky nicht mehr die normalen Hooks im Verzeichnis .git/hooks, sondern es setzt die git-Variable core.hooksPath auf das Verzeichnis .husky.

DarĂĽber hinaus ist eine Datei pre-commit im Verzeichnis .husky mit folgendem Inhalt anzulegen:

#!/usr/bin/env sh
.

echo ""
echo "🚀 Pre-commit Quality Checks"
echo "=============================="
echo ""

echo "đź’Ž Running Prettier (Code Formatting)..."
echo "   ↳ Ensuring consistent code style across all files"
npm run format

if [ $? -ne 0 ]; then
  echo ""
  echo "❌ PRETTIER failed!"
  echo "   ↳ Code formatting could not be applied automatically"
  echo "   đź’ˇ Try running 'npm run format' manually to see the error"
  echo ""
  exit 1
fi

echo "   âś… Code formatting completed successfully"
echo ""

echo "🔍 Running ESLint (Code Quality & Best Practices)..."
echo "   ↳ Checking for code quality issues and potential bugs"
npm run lint:check

if [ $? -ne 0 ]; then
  echo ""
  echo "❌ ESLINT failed!"
  echo "   ↳ Code quality issues found that need manual attention"
  echo "   đź’ˇ Run 'npm run lint' to automatically fix some issues"
  echo "   đź“‹ Review the errors above and fix them before committing"
  echo ""
  exit 1
fi

echo "   âś… Code quality checks passed successfully"
echo ""

echo "📝 Running Vale (Prose Linting)..."
echo "   ↳ Checking documentation and content for style consistency"

# Check if Vale binary exists, if not provide helpful message
if [ -f "./tools/vale" ] || [ -f "./tools/vale.exe" ]; then
  npm run prose:check
  
  if [ $? -ne 0 ]; then
    echo ""
    echo "❌ VALE failed!"
    echo "   ↳ Prose style issues found in documentation"
    echo "   đź’ˇ Review the suggestions above and edit the content manually"
    echo "   đź“‹ Vale cannot auto-fix - manual review required"
    echo ""
    exit 1
  fi
  
  echo "   âś… Prose style checks passed successfully"
else
  echo "   ⚠️  Vale binary not found - skipping prose checks"
  echo "   đź’ˇ Run 'npm run install-vale' to install Vale for prose linting"
fi

echo ""

echo "🎉 All quality checks passed! Ready to commit."
echo "   ↳ Your code is formatted, follows best practices, and prose is well-written"
echo ""

Abschließend ist die Datei noch als ausführbar zu markieren chmod +x ./husky/pre-commit. Beim Ausführen eines git commit werden dann automatisch alle drei Linter-Prüfungen durchgeführt. Sollte dabei einer der Linter auf einen Fehler laufen, wird der Commit abgebrochen. In Ausnahmefällen – etwa bei zeitkritischen Hotfixes oder bei Migrationen von Legacy-Code – kann es notwendig sein zu comitten, auch wenn einer der Linter fehlschlägt. Dies lässt sich mit dem Schalter --no-verify erreichen:

git commit -m "YOUR MESSAGE" --no-verify

Während Pre-Commit Hooks lokale Qualitätskontrollen gewährleisten, sorgen GitHub Actions für zusätzliche Sicherheit in der CI/CD-Pipeline. Warum sollte aber eine zusätzliche CI/CD-Pipeline eingerichtet werden, wenn doch in den Pre-Commit Hooks bereits alles gecheckt wird? Zum einen ist möglich, den Pre-Commit Hook zu umgehen, andererseits können sich neue Situationen ergeben, wenn beispielsweise zwei oder mehr einzelne Branches, die jeder für sich erfolgreich geprüft wurden, zusammengeführt werden.

Das folgende Workflow-Skript wird bei jedem Pull-Request ausgefĂĽhrt:

name: Pull request checks
on:
  pull_request:
    types: [ opened, synchronize, reopened ]
    
jobs:
  pull-request-checks:
    runs-on: [self-hosted, frickeldave-main]
    steps:
      - name: Print branch information
        run: |
          echo "Target branch: ${{ github.base_ref }}"
          echo "Source branch: ${{ github.head_ref }}"

      - name: Skip if not targeting 'main' or 'dev'
        if: github.base_ref != 'main' && github.base_ref != 'dev'
        run: |
          echo "ℹ️  This workflow only applies to PRs targeting 'dev' and 'main' branch."
          echo "Current target: '${{ github.base_ref }}' - skipping checks."
          echo "âś… This PR is not targeting 'dev' or 'main', so it passes."

      - name: Prevent any attempt to merge a branch other than 'dev' into 'main'.
        if: github.base_ref == 'main' && github.head_ref != 'dev'
        run: |
          echo "❌ Merges into 'main' are only allowed from the 'dev' branch."
          echo "❌ Your source branch is '${{ github.head_ref }}'."
          echo "❌ Target branch is '${{ github.base_ref }}'."
          exit 1
          
      - name: Allow merge from dev->main or from any other branch->dev
        if:  (github.base_ref == 'main' && github.head_ref == 'dev') || (github.base_ref == 'dev')
        run: |
          echo "âś… Merging into '${{ github.base_ref }}'. Start linting process..."

          # Install dependencies
          echo "--------------------------------------"
          echo "âž• Install dependencies"
          echo "--------------------------------------"
          echo "Downloading and installing nvm..."
          curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
          export NVM_DIR="$HOME/.nvm"
          [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
          
          export NODE_OPTIONS="--max-old-space-size=8192"
          source ~/.bashrc
          nvm install --lts
          npm ci || { echo "❌ Installation of dependencies failed"; exit 1; }

          # Run ESLint check
          echo "--------------------------------------"
          echo "🔍 Running ESLint check..."
          echo "--------------------------------------"
          npm run lint:check || { echo "❌ ESLint check failed"; exit 1; }
          
          # Run Prettier check
          echo "--------------------------------------"
          echo "✨ Running Prettier check..."
          echo "--------------------------------------"
          npm run format:check || { echo "❌ Prettier check failed"; exit 1; }
          
          # Run Vale prose check
          echo "--------------------------------------"
          echo "📝 Running Vale prose check..."
          echo "--------------------------------------"
          npm run prose || { echo "❌ Vale prose check failed"; exit 1; }
          
          echo "âś… All linting checks passed!"

Darüber hinaus lässt sich das erfolgreiche Ausführen des Skripts auch noch in den Branch-Protection-Regeln eintragen (siehe Abbildung 1).

Eintragen der Branch-Protection-Regeln am Beispiel von GitHub (Abb. 1).

Das mehrstufige Linting-System für Astro-Projekte zeigt, wie ein moderner Entwicklungsworkflow mit Fokus auf das Qualitätsmanagement aussehen kann. Die Kombination von Code-Qualität, Formatierung und Prosa-Linting schafft einen ganzheitlichen Ansatz, der sowohl zu technischen als auch kommunikativen Verbesserungen führt.

Zudem bleibt der Stack offen für weitere Anpassungen: KI-gestütztes Prosa Linting, automatische Übersetzungen oder kontextuelle Verbesserungsvorschläge lassen sich auf Basis der vorgestellten Implementierung hinzufügen.

Dieses Setup empfiehlt sich als Grundlage für professionelle Dokumentationsprojekte und zeigt, dass moderne Entwicklungstools nicht nur die Produktivität steigern, sondern auch zu höherer Qualität der Endprodukte führen.

(map)