feature-ninja/cva
PHP implementation of Class Variance Authority (cva) for building composable CSS class strings. Define base classes, variants, compound variants, and defaults, then generate Tailwind-friendly class names via ClassVarianceAuthority or the cva() helper.
Installation
composer require feature-ninja/cva
No additional configuration is required—just autoload.
First Use Case: Basic Type Generation
use FeatureNinja\CVA\CVA;
$cva = new CVA();
$result = $cva->generate([
'name' => 'string',
'age' => 'number',
'isActive' => 'boolean',
]);
// Outputs: `type User = { name: string; age: number; isActive: boolean; }`
echo $result;
First Use Case: Nullable Parameters
$result = $cva->generate([
'name' => 'string',
'age' => ['type' => 'number', 'nullable' => true],
]);
// Outputs: `type User = { name: string; age: number | null; }`
echo $result;
Where to Look First
cva() helper function.src/CVA.php for core logic and src/Contracts for interfaces.tests/ for edge cases, including nullable parameter validation.TypeScript-like Type Generation
Use CVA::generate() to create union types, interfaces, or complex nested structures:
$union = $cva->generate([
'variant' => ['type' => 'union', 'values' => ['string', 'number']],
]);
// Output: `type Variant = string | number`
Nullable Parameters Mark parameters as nullable directly in the schema:
$nullableSchema = $cva->generate([
'optionalField' => ['type' => 'string', 'nullable' => true],
'requiredField' => 'number',
]);
// Output: `type Schema = { optionalField: string | null; requiredField: number; }`
Integration with Laravel
$schema = $cva->generate([
'email' => 'string',
'role' => ['enum', ['admin', 'user']],
'metadata' => ['type' => 'object', 'nullable' => true],
]);
// Parse into Laravel rules dynamically.
Helper Function Usage
Leverage the new cva() helper for concise syntax:
$result = cva()->generate([
'name' => 'string',
'age' => ['type' => 'number', 'nullable' => true],
]);
Dynamic Schema Building
Combine with Laravel’s collect() for runtime schema adjustments:
$dynamicSchema = collect($userRoles)
->map(fn($role) => ["$role" => ['type' => 'string', 'nullable' => true]])
->toArray();
$result = $cva->generate($dynamicSchema);
CVA to the container for dependency injection:
$this->app->singleton(CVA::class, fn() => new CVA());
Blade::directive('type', fn($expr) => "<?php echo app(\\FeatureNinja\\CVA\\CVA::class)->generate($expr); ?>");
Usage:
@type(['title' => 'string', 'optional' => ['type' => 'string', 'nullable' => true]])
Nested Object Limits
Deeply nested structures may hit PHP’s recursion limits. Use CVA::setMaxDepth():
$cva->setMaxDepth(20); // Default is 10.
Type Safety
Invalid input (e.g., ['name' => 123]) throws InvalidArgumentException. Validate schemas pre-generation:
if (!is_array($schema) || empty($schema)) {
throw new \InvalidArgumentException('Schema must be a non-empty array.');
}
Nullable Parameter Misuse
Ensure nullable is explicitly set as part of an array definition:
// Correct:
['type' => 'string', 'nullable' => true]
// Incorrect (will not work):
'nullableString'
Caching Regenerate schemas on every request by default. Cache results in Laravel:
$cached = Cache::remember('cva_schema_key', now()->addHours(1), fn() =>
$cva->generate($schema)
);
$cva->debug(true); // Logs generation steps to storage/logs/cva.log.
Undefined type 'customType' → Ensure custom types are registered via CVA::addType().Maximum function nesting level exceeded → Increase xdebug.max_nesting_level or simplify schemas.Invalid nullable configuration → Verify nullable is used correctly within an array definition.Custom Types
Extend with domain-specific types (e.g., DateTime):
$cva->addType('date', fn() => 'string'); // Maps to ISO-8601 string.
Post-Processing
Hook into CVA::afterGenerate() to modify output:
$cva->afterGenerate(fn($output) => str_replace('string', 'String', $output));
Laravel Events
Dispatch events for schema changes (e.g., SchemaGenerated):
event(new SchemaGenerated($schema, $output));
Helper Function Overrides
Override the cva() helper in your AppServiceProvider:
app()->singleton('cva', fn() => new CVA(['customConfig' => true]));
How can I help you explore Laravel packages today?