Files
gh-netresearch-claude-code-…/references/directory-structure.md
2025-11-30 08:43:22 +08:00

15 KiB

Directory Structure Standards

Purpose: Validate proper separation of committed configuration vs generated/temporary files in TYPO3 extensions

Why Directory Structure Matters

Proper directory organization prevents common issues:

  • Version Control Hygiene - Only configuration committed, not generated files
  • Build Reproducibility - Clean installs work without artifacts
  • CI/CD Clarity - Clear separation of what's tracked vs generated
  • Onboarding Efficiency - New contributors understand structure instantly
  • Maintenance Simplicity - Updates don't conflict with local artifacts

Without proper structure:

  • Bloated repositories with vendor/, cache files committed
  • Git conflicts in generated files
  • CI failures from missing .gitignore patterns
  • Confusion about what files are source vs generated

TYPO3 Standard Directory Pattern

Build/ - Committed Configuration

Purpose: Project-specific configuration files that define how to build, test, and validate code

Characteristics:

  • Committed to git
  • Shared across all developers
  • Version controlled
  • Defines project standards

Standard Contents:

Build/
├── phpstan.neon              # Static analysis config
├── phpstan-baseline.neon     # Known issues baseline (optional)
├── php-cs-fixer.php          # Code style config
├── rector.php                # Refactoring rules (optional, can be in Build/rector/)
├── phpunit/
│   ├── UnitTests.xml         # Unit test configuration
│   ├── FunctionalTests.xml   # Functional test configuration
│   └── bootstrap.php         # Test bootstrap (if needed)
└── Scripts/
    └── runTests.sh           # Test orchestration script

Alternative Rector Location:

Build/
└── rector/
    └── rector.php            # Rector config in subdirectory

Git Status: All files tracked (not in .gitignore)

.Build/ - Generated/Temporary Files

Purpose: Composer-generated files, caches, runtime artifacts, test outputs

Characteristics:

  • NOT committed to git (gitignored)
  • Generated by composer/build tools
  • Recreatable from configuration
  • Developer-specific caches allowed

Standard Contents:

.Build/
├── bin/                      # Composer bin-dir (phpunit, phpstan, etc.)
├── public/                   # Web root (TYPO3 web-dir)
│   ├── index.php
│   ├── typo3/
│   └── typo3conf/
├── vendor/                   # Composer dependencies
├── .php-cs-fixer.cache       # php-cs-fixer cache file
├── .phpunit.result.cache     # PHPUnit cache
└── var/                      # Runtime cache/logs (optional)

Git Status: Entire directory in .gitignore

Standard .gitignore Entry:

# Composer generated files
.Build/

Validation Checklist

1. Build/ Directory Structure

Check for committed configuration files:

ls -la Build/

Required files for comprehensive quality:

  • Build/phpstan.neon - Static analysis configuration
  • Build/php-cs-fixer.php - Code style configuration
  • Build/phpunit/UnitTests.xml - Unit test configuration
  • Build/Scripts/runTests.sh - Test orchestration

Optional but recommended:

  • Build/phpunit/FunctionalTests.xml - Functional test configuration
  • Build/phpstan-baseline.neon - Known issues baseline
  • Build/rector.php or Build/rector/rector.php - Refactoring rules

Severity if missing: 🟡 Medium - Quality tools not standardized

2. .Build/ Directory Gitignore

Check .gitignore for .Build/ exclusion:

grep -E '^\\.Build/' .gitignore

Expected pattern:

.Build/

Alternative patterns (also valid):

/.Build/
.Build/*

Check for accidental commits:

git ls-files .Build/
# Should return empty - no files from .Build/ should be tracked

Severity if misconfigured: 🔴 High - Can lead to repository bloat

3. Composer Configuration Alignment

Validate composer.json paths match directory structure:

# Extract composer paths
cat composer.json | jq -r '.config."bin-dir"'      # Should be .Build/bin
cat composer.json | jq -r '.config."vendor-dir"'   # Should be .Build/vendor
cat composer.json | jq -r '.extra.typo3.cms."web-dir"'  # Should be .Build/public

Expected composer.json:

{
    "config": {
        "vendor-dir": ".Build/vendor",
        "bin-dir": ".Build/bin"
    },
    "extra": {
        "typo3/cms": {
            "web-dir": ".Build/public"
        }
    }
}

Severity if mismatched: 🔴 High - Build will fail or create wrong structure

4. Quality Tool Configuration Paths

Validate tool configs reference correct cache locations:

# Check PHPUnit cache location
grep -r "\.phpunit\.result\.cache" Build/phpunit/*.xml
# Should reference .Build/.phpunit.result.cache or no cache directive

# Check php-cs-fixer cache
grep -r "cache-file" composer.json Build/php-cs-fixer.php
# Should reference .Build/.php-cs-fixer.cache if specified

# Check PHPStan cache (usually auto-managed)
grep -r "tmpDir" Build/phpstan.neon
# Should reference .Build/phpstan if specified, or auto temp

Example CORRECT patterns:

// composer.json
{
    "scripts": {
        "ci:cgl": [
            "php-cs-fixer fix --cache-file .Build/.php-cs-fixer.cache --dry-run --diff"
        ]
    }
}
// Build/php-cs-fixer.php
return (new PhpCsFixer\Config())
    ->setCacheFile('.Build/.php-cs-fixer.cache')
    ->setRules([...]);
# Build/phpstan.neon
parameters:
    tmpDir: .Build/phpstan

Severity if wrong: 🟡 Medium - Works but creates clutter in Build/

5. Tea Extension Reference

Source: https://github.com/TYPO3BestPractices/tea

Tea Directory Structure:

tea/
├── .gitignore                    # Excludes .Build/
├── Build/
│   ├── phpstan.neon
│   ├── phpstan-baseline.neon
│   ├── php-cs-fixer.php
│   ├── phpunit/
│   │   ├── FunctionalTests.xml
│   │   └── UnitTests.xml
│   └── Scripts/
│       └── runTests.sh
├── .Build/                       # Gitignored, generated by composer
│   ├── bin/
│   ├── public/
│   └── vendor/
└── composer.json                 # Defines .Build/ paths

Tea .gitignore (relevant excerpt):

# Composer-generated files
.Build/
composer.lock

Tea composer.json (relevant excerpt):

{
    "config": {
        "vendor-dir": ".Build/vendor",
        "bin-dir": ".Build/bin"
    },
    "extra": {
        "typo3/cms": {
            "web-dir": ".Build/public"
        }
    }
}

Common Issues and Fixes

Issue 1: Cache Files in Build/ Directory

Diagnosis:

ls -la Build/.php-cs-fixer.cache Build/.phpunit.result.cache
# If these files exist, they're in the WRONG location

Problem: Cache files committed or in wrong directory

Fix:

# Remove from wrong location
rm Build/.php-cs-fixer.cache Build/.phpunit.result.cache

# Update configuration to use .Build/

Update php-cs-fixer config:

// Build/php-cs-fixer.php
return (new PhpCsFixer\Config())
    ->setCacheFile('.Build/.php-cs-fixer.cache')  // ✅ Correct location
    // ...

Update composer scripts:

{
    "scripts": {
        "ci:cgl": [
            "php-cs-fixer fix --cache-file .Build/.php-cs-fixer.cache --dry-run --diff"
        ]
    }
}

Issue 2: .Build/ Files Committed to Git

Diagnosis:

git ls-files .Build/
# Should be empty

Problem: Generated files tracked in git (vendor/, bin/, etc.)

Fix:

# Remove from git tracking
git rm -r --cached .Build/

# Ensure .gitignore has entry
echo ".Build/" >> .gitignore

# Commit the cleanup
git add .gitignore
git commit -m "fix: remove .Build/ from git tracking, add to .gitignore"

Issue 3: Missing Build/ Directory

Diagnosis:

ls -la Build/
# Directory doesn't exist

Problem: No standardized quality tool configuration

Fix:

# Create Build/ directory structure
mkdir -p Build/{phpunit,Scripts}

# Add quality tool configs (see templates below)
# Then commit
git add Build/
git commit -m "feat: add Build/ directory with quality tool configurations"

Issue 4: Rector in Wrong Location

Diagnosis:

# Check for rector.php in project root
ls -la rector.php

# Should be in Build/ or Build/rector/ instead

Problem: Configuration file in project root instead of Build/

Fix:

# Option 1: Move to Build/
mv rector.php Build/rector.php

# Option 2: Move to Build/rector/ (preferred for complex configs)
mkdir -p Build/rector
mv rector.php Build/rector/rector.php

# Update paths in rector.php
# Then commit
git add Build/
git rm rector.php
git commit -m "refactor: move rector config to Build/ directory"

Conformance Report Integration

When Evaluating Directory Structure:

In "Best Practices" Section:

### Directory Structure

**Analysis:**

- ✅ Build/ directory present with committed configurations
- ✅ Build/phpstan.neon, Build/php-cs-fixer.php present
- ✅ Build/phpunit/ directory with test configs
- ✅ Build/Scripts/runTests.sh present
- ✅ .Build/ properly gitignored (entire directory)
- ✅ Composer paths correctly reference .Build/
- ✅ Cache files located in .Build/, not Build/
- ✅ No .Build/ files committed to git

**Or with issues:**

- ❌ Cache files in wrong location
  - Files: Build/.php-cs-fixer.cache, Build/.phpunit.result.cache
  - Expected: .Build/.php-cs-fixer.cache, .Build/.phpunit.result.cache
  - Severity: Medium
  - Fix: Move cache files to .Build/ and update configs

- ❌ .Build/ files committed to git
  - Files: .Build/vendor/, .Build/bin/
  - Command: `git ls-files .Build/` shows tracked files
  - Severity: High
  - Fix: `git rm -r --cached .Build/` and ensure .gitignore has `.Build/`

- ⚠️  Missing Build/ directory
  - Impact: No standardized quality tool configuration
  - Severity: Medium
  - Recommendation: Create Build/ with phpstan.neon, php-cs-fixer.php, phpunit configs

Scoring Impact

Best Practices Score Deductions:

Issue Severity Score Impact
.Build/ files committed High -4 points
Cache files in Build/ Medium -2 points
Missing .gitignore for .Build/ High -3 points
Composer paths don't match structure High -3 points
Missing Build/ directory Medium -2 points
Rector/configs in project root Low -1 point

Maximum deduction for directory issues: -6 points (out of 20 for Best Practices)

Automated Validation Script

Create scripts/validate-directory-structure.sh:

#!/bin/bash

set -e

echo "🔍 Validating directory structure..."

ISSUES=0

# Check 1: .Build/ should be gitignored
if ! grep -qE '^\\.Build/' .gitignore; then
    echo "❌ .gitignore missing '.Build/' entry"
    ISSUES=$((ISSUES + 1))
else
    echo "✅ .Build/ properly gitignored"
fi

# Check 2: No .Build/ files should be tracked
TRACKED_BUILD=$(git ls-files .Build/ 2>/dev/null | wc -l)
if [ "${TRACKED_BUILD}" -gt 0 ]; then
    echo "❌ .Build/ files are committed to git:"
    git ls-files .Build/
    ISSUES=$((ISSUES + 1))
else
    echo "✅ No .Build/ files tracked in git"
fi

# Check 3: Build/ directory should exist
if [ ! -d "Build" ]; then
    echo "⚠️  Build/ directory missing"
    ISSUES=$((ISSUES + 1))
else
    echo "✅ Build/ directory exists"

    # Check for standard files
    [ -f "Build/phpstan.neon" ] && echo "  ✅ phpstan.neon" || echo "  ⚠️  phpstan.neon missing"
    [ -f "Build/php-cs-fixer.php" ] && echo "  ✅ php-cs-fixer.php" || echo "  ⚠️  php-cs-fixer.php missing"
    [ -d "Build/phpunit" ] && echo "  ✅ phpunit/" || echo "  ⚠️  phpunit/ missing"
    [ -f "Build/Scripts/runTests.sh" ] && echo "  ✅ runTests.sh" || echo "  ⚠️  runTests.sh missing"
fi

# Check 4: Cache files should NOT be in Build/
if [ -f "Build/.php-cs-fixer.cache" ] || [ -f "Build/.phpunit.result.cache" ]; then
    echo "❌ Cache files in wrong location (Build/ instead of .Build/):"
    ls -la Build/.*.cache 2>/dev/null || true
    ISSUES=$((ISSUES + 1))
else
    echo "✅ No cache files in Build/"
fi

# Check 5: Composer paths should reference .Build/
BIN_DIR=$(jq -r '.config."bin-dir" // ".Build/bin"' composer.json)
VENDOR_DIR=$(jq -r '.config."vendor-dir" // ".Build/vendor"' composer.json)
WEB_DIR=$(jq -r '.extra.typo3.cms."web-dir" // ".Build/public"' composer.json)

if [ "${BIN_DIR}" = ".Build/bin" ]; then
    echo "✅ Composer bin-dir: ${BIN_DIR}"
else
    echo "⚠️  Composer bin-dir: ${BIN_DIR} (expected .Build/bin)"
    ISSUES=$((ISSUES + 1))
fi

if [ "${VENDOR_DIR}" = ".Build/vendor" ]; then
    echo "✅ Composer vendor-dir: ${VENDOR_DIR}"
else
    echo "⚠️  Composer vendor-dir: ${VENDOR_DIR} (expected .Build/vendor)"
    ISSUES=$((ISSUES + 1))
fi

if [ "${WEB_DIR}" = ".Build/public" ]; then
    echo "✅ TYPO3 web-dir: ${WEB_DIR}"
else
    echo "⚠️  TYPO3 web-dir: ${WEB_DIR} (expected .Build/public)"
    ISSUES=$((ISSUES + 1))
fi

echo ""
if [ ${ISSUES} -eq 0 ]; then
    echo "✅ Directory structure validation complete - no issues found"
    exit 0
else
    echo "❌ Directory structure validation found ${ISSUES} issue(s)"
    exit 1
fi

Quick Reference Checklist

When evaluating directory structure:

□ .gitignore contains .Build/ entry
□ Build/ directory exists and contains configs
□ Build/phpstan.neon exists
□ Build/php-cs-fixer.php exists
□ Build/phpunit/ directory exists with XML configs
□ Build/Scripts/runTests.sh exists
□ .Build/ is NOT tracked in git (git ls-files .Build/ is empty)
□ Cache files are in .Build/, not Build/
□ Composer bin-dir = .Build/bin
□ Composer vendor-dir = .Build/vendor
□ TYPO3 web-dir = .Build/public
□ No configuration files in project root (rector.php, phpstan.neon, etc.)

Configuration File Templates

Build/phpstan.neon

includes:
    - vendor/phpstan/phpstan-strict-rules/rules.neon
    - vendor/saschaegerer/phpstan-typo3/extension.neon

parameters:
    level: max
    paths:
        - Classes
        - Tests
    tmpDir: .Build/phpstan
    reportUnmatchedIgnoredErrors: true

Build/php-cs-fixer.php

<?php

declare(strict_types=1);

$finder = (new PhpCsFixer\Finder())
    ->in(__DIR__ . '/../Classes')
    ->in(__DIR__ . '/../Tests');

return (new PhpCsFixer\Config())
    ->setRules([
        '@PSR12' => true,
        '@PhpCsFixer' => true,
        'declare_strict_types' => true,
    ])
    ->setCacheFile('.Build/.php-cs-fixer.cache')
    ->setRiskyAllowed(true)
    ->setFinder($finder);

Build/rector/rector.php

<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\ValueObject\PhpVersion;
use Ssch\TYPO3Rector\Set\Typo3LevelSetList;

return RectorConfig::configure()
    ->withPaths([
        __DIR__ . '/../../Classes/',
        __DIR__ . '/../../Tests/',
    ])
    ->withPhpVersion(PhpVersion::PHP_82)
    ->withPhpSets(true)
    ->withSets([
        Typo3LevelSetList::UP_TO_TYPO3_13,
    ]);

Resources