Files
2025-11-30 08:43:13 +08:00

12 KiB

Composer.json Validation Standards (TYPO3 v13)

Source: TYPO3 Core API Reference v13.4 - FileStructure/ComposerJson.html Purpose: Complete validation rules for composer.json in TYPO3 extensions

Mandatory Fields

name

Format: <vendor>/<dashed-extension-key>

Examples:

"name": "vendor-name/my-extension"
"name": "johndoe/some-extension"

Validation:

jq -r '.name' composer.json | grep -E '^[a-z0-9-]+/[a-z0-9-]+$' && echo "✅ Valid" || echo "❌ Invalid format"

type

Required Value: typo3-cms-extension (for third-party extensions)

Validation:

jq -r '.type' composer.json | grep -q "typo3-cms-extension" && echo "✅ Correct type" || echo "❌ Wrong type"

description

Format: Single-line summary describing what the extension does

Requirements:

  • Clear, concise description of extension functionality
  • Should identify the vendor/company for professional extensions
  • Avoid vague descriptions like "An extension" or "Utility tools"

Good Examples:

"description": "Adds image support to CKEditor5 RTE - by Netresearch"
"description": "TYPO3 extension for advanced content management by Vendor GmbH"
"description": "Provides custom form elements for newsletter subscription"

Bad Examples:

"description": "Extension"  // Too vague
"description": "Some tools"  // Meaningless
"description": ""  // Empty

Validation:

# Check description exists and is not empty
jq -r '.description' composer.json | grep -q . && echo "✅ Has description" || echo "❌ Missing description"

# Check description length (should be meaningful, >20 chars)
DESC_LEN=$(jq -r '.description | length' composer.json)
[[ $DESC_LEN -gt 20 ]] && echo "✅ Description is meaningful" || echo "⚠️  Description too short"

license

Recommended: GPL-2.0-only or GPL-2.0-or-later

Validation:

jq -r '.license' composer.json | grep -qE "GPL-2.0-(only|or-later)" && echo "✅ GPL license" || echo "⚠️  Check license"

require

Minimum: Must specify typo3/cms-core with version constraints

Version Constraint Format:

  • ^12.4 || ^13.4 - Multiple major versions (recommended for v12/v13 compat)
  • ^12.4 - Single major version
  • >=12.4 - NO upper bound (not recommended)

Validation:

# Check typo3/cms-core present
jq -r '.require["typo3/cms-core"]' composer.json | grep -q . && echo "✅ TYPO3 core required" || echo "❌ Missing typo3/cms-core"

# Check for upper bound (^ or specific upper version)
jq -r '.require["typo3/cms-core"]' composer.json | grep -qE '(\^|[0-9]+\.[0-9]+\.[0-9]+-[0-9]+\.[0-9]+\.[0-9]+)' && echo "✅ Has upper bound" || echo "⚠️  Missing upper bound"

autoload

Format: PSR-4 mapping to Classes/ directory

Example:

"autoload": {
    "psr-4": {
        "Vendor\\ExtensionName\\": "Classes/"
    }
}

Validation:

jq -r '.autoload["psr-4"]' composer.json | grep -q "Classes" && echo "✅ PSR-4 autoload configured" || echo "❌ Missing autoload"

extra.typo3/cms.extension-key

Required: Maps to underscored extension key

Example:

"extra": {
    "typo3/cms": {
        "extension-key": "my_extension"
    }
}

Validation:

jq -r '.extra."typo3/cms"."extension-key"' composer.json | grep -q . && echo "✅ Extension key defined" || echo "❌ Missing extension-key"

authors

Format: Array of author objects with name, email, role, homepage

Example:

"authors": [
    {
        "name": "Developer Name",
        "email": "developer@company.com",
        "role": "Developer",
        "homepage": "https://www.company.com/"
    }
]

Required Sub-Fields:

Field Format Purpose
name String Developer's full name
email Email address Contact email
role String Developer, Maintainer, Lead Developer
homepage URL Company or personal website

Validation:

# Check authors array exists
jq -r '.authors' composer.json | grep -q "name" && echo "✅ Has authors" || echo "⚠️  Missing authors"

# Check authors have email
jq -r '.authors[].email' composer.json | grep -q "@" && echo "✅ Has author emails" || echo "⚠️  Missing author emails"

# Check authors have homepage
jq -r '.authors[].homepage' composer.json | grep -q "http" && echo "✅ Has author homepage" || echo "⚠️  Missing author homepage"

homepage

Format: URL to project repository or documentation

Example:

"homepage": "https://github.com/vendor/extension-name"

Validation:

jq -r '.homepage' composer.json | grep -qE "^https?://" && echo "✅ Has homepage" || echo "⚠️  Missing homepage"

support

Format: Object with support channels

Example:

"support": {
    "issues": "https://github.com/vendor/extension/issues",
    "source": "https://github.com/vendor/extension"
}

Validation:

jq -r '.support.issues' composer.json | grep -q "http" && echo "✅ Has issues URL" || echo "⚠️  Missing issues URL"

keywords

Format: Array of relevant keywords for discoverability

Example:

"keywords": [
    "TYPO3",
    "extension",
    "content",
    "management"
]

Validation:

jq -r '.keywords | length' composer.json | grep -qE '^[1-9]' && echo "✅ Has keywords" || echo "⚠️  Missing keywords"

Complete Required Fields Checklist

Mandatory (MUST have):

  • name - vendor/package format
  • type - must be typo3-cms-extension
  • description - clear, concise description
  • license - SPDX identifier (GPL-2.0-or-later, AGPL-3.0-or-later)
  • require.typo3/cms-core - with upper bound constraint
  • require.php - PHP version constraint
  • autoload.psr-4 - mapping to Classes/
  • extra.typo3/cms.extension-key - underscored extension key

Recommended (SHOULD have):

  • authors - with name, email, role, homepage
  • homepage - project repository URL
  • support.issues - issue tracker URL
  • keywords - for discoverability

Deprecated Properties

replace with typo3-ter vendor

Status: DEPRECATED - Legacy TER integration approach

Detection:

jq -r '.replace' composer.json | grep -q "typo3-ter" && echo "⚠️  Deprecated: typo3-ter in replace" || echo "✅ No deprecated replace"

replace with "ext_key": "self.version"

Status: DEPRECATED - Legacy dependency specification

Detection:

jq -r '.replace' composer.json | grep -qE '"[a-z_]+": "self.version"' && echo "⚠️  Deprecated: self.version replace" || echo "✅ No self.version"

TYPO3 v12-v13 Version Constraints

"require": {
    "typo3/cms-core": "^12.4 || ^13.4",
    "php": "^8.1"
}

PHP Version Constraints

"require": {
    "php": "^8.1"  // TYPO3 v12: PHP 8.1-8.4
}

Validation:

# Check PHP constraint
jq -r '.require.php' composer.json | grep -qE '\^8\.[1-4]' && echo "✅ Valid PHP constraint" || echo "⚠️  Check PHP version"

Synchronization with ext_emconf.php

Critical: composer.json and ext_emconf.php must have matching dependency constraints.

Mapping:

composer.json ext_emconf.php
require.typo3/cms-core constraints.depends.typo3
require.php constraints.depends.php
require.* constraints.depends.*

Example Synchronization:

composer.json:

"require": {
    "typo3/cms-core": "^12.4 || ^13.4",
    "php": "^8.1",
    "typo3/cms-fluid": "^12.4 || ^13.4"
}

ext_emconf.php:

'constraints' => [
    'depends' => [
        'typo3' => '12.4.0-13.4.99',
        'php' => '8.1.0-8.4.99',
        'fluid' => '12.4.0-13.4.99',
    ],
],

Complete Validation Script

#!/bin/bash
# validate-composer.sh

ERRORS=0

echo "=== Composer.json Validation ===="

# Check mandatory fields
jq -r '.name' composer.json > /dev/null 2>&1 || { echo "❌ Missing 'name'"; ((ERRORS++)); }
jq -r '.type' composer.json | grep -q "typo3-cms-extension" || { echo "❌ Wrong or missing 'type'"; ((ERRORS++)); }
jq -r '.description' composer.json | grep -q . || { echo "❌ Missing 'description'"; ((ERRORS++)); }

# Check description is meaningful (>20 chars)
DESC_LEN=$(jq -r '.description | length' composer.json 2>/dev/null)
[[ $DESC_LEN -lt 20 ]] && { echo "⚠️  Description too short (should be >20 chars)"; ((WARNINGS++)); }

# Check typo3/cms-core
jq -r '.require["typo3/cms-core"]' composer.json | grep -q . || { echo "❌ Missing typo3/cms-core"; ((ERRORS++)); }

# Check version constraints have upper bounds
jq -r '.require["typo3/cms-core"]' composer.json | grep -qE '(\^|[0-9]+\.[0-9]+\.[0-9]+-[0-9]+\.[0-9]+\.[0-9]+)' || { echo "⚠️  TYPO3 constraint missing upper bound"; ((ERRORS++)); }

# Check autoload
jq -r '.autoload["psr-4"]' composer.json | grep -q "Classes" || { echo "❌ Missing PSR-4 autoload"; ((ERRORS++)); }

# Check extension-key
jq -r '.extra."typo3/cms"."extension-key"' composer.json | grep -q . || { echo "❌ Missing extension-key"; ((ERRORS++)); }

# Check for deprecated replace
jq -r '.replace' composer.json 2>/dev/null | grep -q "typo3-ter\|self.version" && echo "⚠️  Deprecated replace property found"

echo ""
echo "Validation complete: $ERRORS critical errors"
exit $ERRORS

require-dev

Purpose: Development dependencies not needed in production

Example:

"require-dev": {
    "typo3/coding-standards": "^0.7",
    "phpstan/phpstan": "^1.10",
    "phpunit/phpunit": "^10.0"
}

suggest

Purpose: Optional packages that enhance functionality

Example:

"suggest": {
    "typo3/cms-filelist": "For file browser functionality",
    "typo3/cms-reactions": "For webhook support"
}

Best Practices

  1. Packagist Publication: Publishing to Packagist makes extensions available in TYPO3 Extension Repository automatically
  2. Documentation Rendering: composer.json is REQUIRED for extensions with documentation on docs.typo3.org
  3. Version Constraint Strategy:
    • Use ^ for flexible upper bounds
    • Specify both major version ranges for v12/v13 compatibility
    • Always include upper bounds (avoid >= without upper limit)
  4. Namespace Alignment: PSR-4 namespace should match vendor/extension structure
  5. Composer Priority: Composer-based installations prioritize composer.json over ext_emconf.php for dependency resolution

Common Violations and Fixes

Missing extra.typo3/cms.extension-key

Before:

{
    "name": "vendor/my-extension",
    "type": "typo3-cms-extension"
}

After:

{
    "name": "vendor/my-extension",
    "type": "typo3-cms-extension",
    "extra": {
        "typo3/cms": {
            "extension-key": "my_extension"
        }
    }
}

Version Constraint Without Upper Bound

Before:

"require": {
    "typo3/cms-core": ">=12.4"
}

After:

"require": {
    "typo3/cms-core": "^12.4 || ^13.4"
}

Deprecated replace Property

Before:

"replace": {
    "typo3-ter/my-extension": "self.version"
}

After:

// Remove replace property entirely

Additional Validation Commands

Check all required dependencies have upper bounds

jq -r '.require | to_entries[] | select(.value | test(">=") and (test("\\^") | not)) | .key' composer.json

Verify package type

jq -r '.type' composer.json | grep -q "typo3-cms-extension" && echo "✅" || echo "❌ Wrong package type"

Check PSR-4 namespace format

jq -r '.autoload["psr-4"] | keys[]' composer.json | grep -E '^[A-Z][a-zA-Z0-9]*\\\\[A-Z][a-zA-Z0-9]*\\\\$' && echo "✅ Valid namespace" || echo "⚠️  Check namespace format"

Validate JSON syntax

jq . composer.json > /dev/null && echo "✅ Valid JSON" || echo "❌ JSON syntax error"