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"
Recommended Fields (Professional Extensions)
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 formattype- must betypo3-cms-extensiondescription- clear, concise descriptionlicense- SPDX identifier (GPL-2.0-or-later, AGPL-3.0-or-later)require.typo3/cms-core- with upper bound constraintrequire.php- PHP version constraintautoload.psr-4- mapping to Classes/extra.typo3/cms.extension-key- underscored extension key
Recommended (SHOULD have):
authors- with name, email, role, homepagehomepage- project repository URLsupport.issues- issue tracker URLkeywords- 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
Recommended Format
"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
Optional but Recommended Fields
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
- Packagist Publication: Publishing to Packagist makes extensions available in TYPO3 Extension Repository automatically
- Documentation Rendering:
composer.jsonis REQUIRED for extensions with documentation on docs.typo3.org - 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)
- Use
- Namespace Alignment: PSR-4 namespace should match vendor/extension structure
- Composer Priority: Composer-based installations prioritize
composer.jsonoverext_emconf.phpfor 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"