diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4037284..3f128f2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,6 +18,7 @@ jobs: fail-fast: false matrix: php: [ "8.1", "8.2" ] + laravel: [ "^9.0", "^10.0" ] dependency-version: [ prefer-lowest, prefer-stable ] name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} @@ -30,6 +31,8 @@ jobs: with: php-version: ${{ matrix.php }} - name: 🔮 Install dependencies - run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction + run: | + composer require "laravel/framework:${{ matrix.laravel }}" --dev --no-interaction --no-update + composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction - name: 🕵️‍♂️ Run Pest Tests run: composer test diff --git a/README.md b/README.md index e8e4597..6e3fec5 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,76 @@ -# tailwind-merge-php +

Social Card of Tailwind Merge PHP

+ +# Efficiently merge Tailwind CSS classes in PHP without style conflicts [![Latest Version](https://img.shields.io/github/release/yieldstudio/tailwind-merge-php?style=flat-square)](https://github.com/yieldstudio/tailwind-merge-php/releases) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/yieldstudio/tailwind-merge-php/tests.yml?branch=main)](https://github.com/yieldstudio/tailwind-merge-php/actions/workflows/tests.yml) [![Total Downloads](https://img.shields.io/packagist/dt/yieldstudio/tailwind-merge-php?style=flat-square)](https://packagist.org/packages/yieldstudio/tailwind-merge-php) -Utility function to efficiently merge [Tailwind CSS](https://tailwindcss.com) classes in PHP without style conflicts. - -> Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable. +This package allows you to merge multiple [Tailwind CSS](https://tailwindcss.com) classes and automatically resolves conflicts between them without headaches. tailwind-merge-php is a PHP port of the excellent [tailwind-merge](https://github.com/dcastil/tailwind-merge) created by [dcastil](https://github.com/dcastil). - - Supports Tailwind v3.0 up to v3.3 -- First-class support for laravel +- First-class support for [Laravel](https://laravel.com/) + +## Installation + + composer require yieldstudio/tailwind-merge-php + +## Usage ```php use YieldStudio\TailwindMerge\TailwindMerge; +use YieldStudio\TailwindMerge\TailwindMergeConfig; -TailwindMerge::instance()->merge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]'); -// → 'hover:bg-dark-red p-3 bg-[#B91C1C]' +// Singleton +$twMerge = TailwindMerge::instance(); +$twMerge->merge('w-8 h-8 rounded-full rounded-lg'); // w-8 h-8 rounded-lg -tw_merge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]'); -// → 'hover:bg-dark-red p-3 bg-[#B91C1C]' +// Custom instance +$twMerge = new TailwindMerge(TailwindMergeConfig::default()); // Config is optional +$twMerge->merge('w-8 h-8 rounded-full rounded-lg'); // w-8 h-8 rounded-lg ``` -> ✍️ Complete documentation is being written +## Laravel Support -## Installation - composer require yieldstudio/tailwind-merge-php +You can publish the configuration file with: + +```shell +php artisan vendor:publish --provider="YieldStudio\TailwindMerge\Laravel\TailwindMergeServiceProvider" +``` + +### Using the `tw` helper + +```php +tw('w-8 h-8 rounded-full rounded-lg'); // w-8 h-8 rounded-lg +``` +### Using Blade directive + +```php +
+ +// will be +
+``` + +### Using Blade components + +```php +// avatar.blade.php +
tw('w-8 h-8 rounded-full') }}>
+ +// header.blade.php + + +// will be +
+``` + +## Configuration & plugins + +> ✍️ Complete documentation is being written ## Unit tests diff --git a/art/socialcard.jpg b/art/socialcard.jpg new file mode 100644 index 0000000..0b1ed26 Binary files /dev/null and b/art/socialcard.jpg differ diff --git a/composer.json b/composer.json index 29bb063..1ebd303 100644 --- a/composer.json +++ b/composer.json @@ -14,9 +14,6 @@ "php" ], "autoload": { - "files": [ - "src/helpers.php" - ], "psr-4": { "YieldStudio\\TailwindMerge\\": "src/" } @@ -38,17 +35,17 @@ "types": "phpstan analyse" }, "require": { - "php": ">=8.1", - "illuminate/collections": "^10.4" + "php": ">=8.1" }, "require-dev": { - "pestphp/pest": "^2.2", - "illuminate/support": "^10.4", + "pestphp/pest": "^v1|^v2.2.3", "laravel/pint": "^1.10", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^1.10", + "laravel/framework": "^9|^10", + "orchestra/testbench": "7.*|8.*" }, "suggest": { - "illuminate/support": "Required to use with Laravel." + "laravel/framework": "Want to use Tailwind Merge PHP with Laravel ?" }, "config": { "allow-plugins": { diff --git a/src/ClassContext.php b/src/ClassContext.php new file mode 100644 index 0000000..28ddc1c --- /dev/null +++ b/src/ClassContext.php @@ -0,0 +1,18 @@ +validators->push(new ClassValidator( + $classPart->validators[] = new ClassValidator( $classGroupId, $classDefinition - )); + ); continue; } @@ -72,18 +72,14 @@ protected static function getPart(ClassPart $classPart, string $path): ClassPart $currentClassPartObject = $classPart; foreach (explode(self::CLASS_PART_SEPARATOR, $path) as $pathPart) { - if (!$currentClassPartObject) { - return $classPart; - } - - if (! $currentClassPartObject->nextPart->has($pathPart)) { - $currentClassPartObject->nextPart->put($pathPart, new ClassPart()); + if (! array_key_exists($pathPart, $currentClassPartObject->nextPart)) { + $currentClassPartObject->nextPart[$pathPart] = new ClassPart(); } - $currentClassPartObject = $currentClassPartObject->nextPart->get($pathPart); + $currentClassPartObject = $currentClassPartObject->nextPart[$pathPart]; } - return $currentClassPartObject ?? $classPart; + return $currentClassPartObject; } protected static function getPrefixedClassGroups(array $classGroups, ?string $prefix): array diff --git a/src/ClassPart.php b/src/ClassPart.php index c02a448..70965e1 100644 --- a/src/ClassPart.php +++ b/src/ClassPart.php @@ -4,25 +4,20 @@ namespace YieldStudio\TailwindMerge; -use Illuminate\Support\Collection; - final class ClassPart { /** - * @var Collection + * @var array */ - public readonly Collection $nextPart; + public array $nextPart = []; /** - * @var Collection + * @var ClassValidator[] */ - public readonly Collection $validators; + public array $validators = []; public function __construct(public ?string $classGroupId = null) { - - $this->nextPart = new Collection(); - $this->validators = new Collection(); } public function setClassGroupId(string $classGroupId): static diff --git a/src/ClassUtils.php b/src/ClassUtils.php index 353a05c..688599d 100644 --- a/src/ClassUtils.php +++ b/src/ClassUtils.php @@ -137,23 +137,26 @@ protected function getGroupRecursive(array $classParts, ClassPart $classPart): ? } $currentClassPart = $classParts[0]; - $nextClassPartObject = $classPart->nextPart->get($currentClassPart); + $nextClassPartObject = $classPart->nextPart[$currentClassPart] ?? null; $classGroupFromNextClassPart = $nextClassPartObject ? $this->getGroupRecursive(array_slice($classParts, 1), $nextClassPartObject) : null; if ($classGroupFromNextClassPart) { return $classGroupFromNextClassPart; } - if ($classPart->validators->isEmpty()) { + if (empty($classPart->validators)) { return null; } $classRest = implode('-', $classParts); - return $classPart - ->validators - ->first(fn (ClassValidator $classValidator) => $classValidator->rule->execute($classRest)) - ?->classGroupId ?? null; + foreach ($classPart->validators as $classValidator) { + if ($classValidator->rule->execute($classRest)) { + return $classValidator->classGroupId; + } + } + + return null; } protected function getGroupIdForArbitraryProperty(string $className): ?string diff --git a/src/Laravel/Facades/TailwindMerge.php b/src/Laravel/Facades/TailwindMerge.php new file mode 100644 index 0000000..8621a65 --- /dev/null +++ b/src/Laravel/Facades/TailwindMerge.php @@ -0,0 +1,18 @@ +app->singleton(TailwindMerge::class, function (): TailwindMerge { + return new TailwindMerge( + TailwindMergeConfig::fromArray( + (array) config('tailwind-merge', []), + config('tailwind-merge.strategy', 'merge') === 'merge' + ) + ); + }); + + $this->app->alias(TailwindMerge::class, 'tailwind-merge'); + + require_once __DIR__ . '/helpers.php'; + } + public function boot(): void { + if ($this->app->runningInConsole()) { + $this->publishes([ + __DIR__ . '/config/tailwind-merge.php' => config_path('tailwind-merge.php'), + ]); + } + + ComponentAttributeBag::macro('tw', function (...$args) { + /* @phpstan-ignore-next-line */ + $this->attributes['class'] = app(TailwindMerge::class)->merge( + $args, + /* @phpstan-ignore-next-line */ + $this->attributes['class'] ?? '' + ); + + return $this; + }); + Blade::directive('tw', function ($expression) { $expression = is_null($expression) ? '()' : $expression; - return "class=\"\""; + return "class=\"\""; }); } + + public function provides(): array + { + return [ + TailwindMerge::class, + 'tailwind-merge', + ]; + } } diff --git a/src/Laravel/config/tailwind-merge.php b/src/Laravel/config/tailwind-merge.php new file mode 100644 index 0000000..0648850 --- /dev/null +++ b/src/Laravel/config/tailwind-merge.php @@ -0,0 +1,106 @@ + 'merge', + + /* + |-------------------------------------------------------------------------- + | LRU Cache Size + |-------------------------------------------------------------------------- + | + | Integer indicating size of LRU cache used for memoizing results. + | Cache might be up to twice as big as `cacheSize`. + | To disable cache, set to 0. + */ + + 'cache_size' => 5000, + + /* + |-------------------------------------------------------------------------- + | Separator + |-------------------------------------------------------------------------- + | + | Custom separator for modifiers in Tailwind classes. + | See https://tailwindcss.com/docs/configuration#separator + */ + + 'separator' => ':', + + /* + |-------------------------------------------------------------------------- + | Prefix + |-------------------------------------------------------------------------- + | + | Prefix added to Tailwind-generated classes. + | See https://tailwindcss.com/docs/configuration#prefix + */ + + 'prefix' => null, + + /* + |-------------------------------------------------------------------------- + | Theme + |-------------------------------------------------------------------------- + | + | Theme scales used in classGroups. + | The keys are the same as in the Tailwind config but the values are sometimes defined more broadly. + */ + + 'theme' => [], + + /* + |-------------------------------------------------------------------------- + | Class Groups + |-------------------------------------------------------------------------- + | + | Object with groups of classes. + | Example : { + | // Creates group of classes `group`, `of` and `classes` + | 'group-id': ['group', 'of', 'classes'], + | // Creates group of classes `look-at-me-other` and `look-at-me-group`. + | 'other-group': [{ 'look-at-me': ['other', 'group']}] + | } + */ + + 'class_groups' => [], + + /* + |-------------------------------------------------------------------------- + | Conflicting Class Groups + |-------------------------------------------------------------------------- + | + | Conflicting classes across groups. + | The key is ID of class group which creates conflict, values are IDs of class groups which receive a conflict. + | A class group is ID is the key of a class group in classGroups object. + | + | Example : { gap: ['gap-x', 'gap-y'] } + */ + + 'conflicting_class_groups' => [], + + /* + |-------------------------------------------------------------------------- + | Conflicting Class Group Modifiers + |-------------------------------------------------------------------------- + | + | A class group ID is the key of a class group in classGroups object. + | + | Example : { 'font-size': ['leading'] } + */ + + 'conflicting_class_group_modifiers' => [], + +]; diff --git a/src/Laravel/helpers.php b/src/Laravel/helpers.php new file mode 100644 index 0000000..cdbe441 --- /dev/null +++ b/src/Laravel/helpers.php @@ -0,0 +1,18 @@ +merge(...$classLists); + } +} diff --git a/src/TailwindMerge.php b/src/TailwindMerge.php index 4faf317..b4e54a8 100644 --- a/src/TailwindMerge.php +++ b/src/TailwindMerge.php @@ -5,22 +5,21 @@ namespace YieldStudio\TailwindMerge; use Closure; -use Illuminate\Support\Collection; use YieldStudio\TailwindMerge\Interfaces\TailwindMergePlugin; final class TailwindMerge { - protected const SPLIT_CLASSES_REGEX = '/\s+/'; + private const SPLIT_CLASSES_REGEX = '/\s+/'; - protected const IMPORTANT_MODIFIER = '!'; + private const IMPORTANT_MODIFIER = '!'; - protected LruCache $cache; + private LruCache $cache; - protected ClassUtils $classUtils; + private ClassUtils $classUtils; - protected TailwindMergeConfig $config; + private TailwindMergeConfig $config; - protected static ?TailwindMerge $instance = null; + private static ?TailwindMerge $instance = null; public function __construct(?TailwindMergeConfig $config = null) { @@ -62,104 +61,36 @@ public function extend(Closure|TailwindMergePlugin ...$plugins): TailwindMerge public static function instance(): TailwindMerge { - if (! self::$instance) { + if (!self::$instance) { self::$instance = new TailwindMerge(); } return self::$instance; } - protected function mergeClassList(string $classList): string + private function mergeClassList(string $classList): string { - /** - * Set of classGroupIds in following format: - * `{importantModifier}{variantModifiers}{classGroupId}` - * - * @example 'float' - * @example 'hover:focus:bg-color' - * @example 'md:!pr' - */ - $classGroupsInConflict = new Collection(); - - return (new Collection((array) preg_split(self::SPLIT_CLASSES_REGEX, trim($classList)))) - ->map(function ($originalClassName) { - $modifiersContext = $this->classUtils->splitModifiers((string) $originalClassName); - - $classGroupId = $this->classUtils->getClassGroupId( - is_null($modifiersContext->maybePostfixModifierPosition) - ? $modifiersContext->baseClassName - : substr($modifiersContext->baseClassName, 0, $modifiersContext->maybePostfixModifierPosition) - ); - - $hasPostfixModifier = is_int($modifiersContext->maybePostfixModifierPosition); - - if (! $classGroupId) { - if (is_null($modifiersContext->maybePostfixModifierPosition)) { - return [ - 'isTailwindClass' => false, - 'originalClassName' => $originalClassName, - ]; - } - - $classGroupId = $this->classUtils->getClassGroupId($modifiersContext->baseClassName); + $classes = (array)preg_split(self::SPLIT_CLASSES_REGEX, trim($classList)); - if (! $classGroupId) { - return [ - 'isTailwindClass' => false, - 'originalClassName' => $originalClassName, - ]; - } - - $hasPostfixModifier = false; - } + $classes = array_map([$this, 'determineClassContext'], $classes); - $variantModifier = implode(':', $this->classUtils->sortModifiers($modifiersContext->modifiers)); - $modifierId = $modifiersContext->hasImportantModifier - ? $variantModifier.self::IMPORTANT_MODIFIER - : $variantModifier; - - return [ - 'isTailwindClass' => true, - 'modifierId' => $modifierId, - 'classGroupId' => $classGroupId, - 'originalClassName' => $originalClassName, - 'hasPostfixModifier' => $hasPostfixModifier, - ]; - }) - // Last class in conflict wins, so we need to filter conflicting classes in reverse order. - ->reverse() - ->filter(function (array $parsed) use ($classGroupsInConflict) { - if (! $parsed['isTailwindClass']) { - return true; - } + // Last class in conflict wins, so we need to filter conflicting classes in reverse order. + $classes = array_reverse($classes); - /** @phpstan-ignore-next-line */ - $classId = $parsed['modifierId'].$parsed['classGroupId']; - if ($classGroupsInConflict->contains($classId)) { - return false; - } + $classes = $this->filterConflictingClasses($classes); - $classGroupsInConflict->push($classId); + $classes = array_map(fn (ClassContext $context) => $context->originalClassName, $classes); - /** @phpstan-ignore-next-line */ - $conflicts = $this->classUtils->getConflictingClassGroupIds($parsed['classGroupId'], $parsed['hasPostfixModifier']); - foreach ($conflicts as $group) { - /** @phpstan-ignore-next-line */ - $classGroupsInConflict->push($parsed['modifierId'].$group); - } + // Reorder in the good way + $classes = array_reverse($classes); - return true; - }) - ->reverse() - ->map(fn (array $parsed) => $parsed['originalClassName']) - ->join(' '); + return implode(' ', $classes); } /** - * @param string|array|array ...$classList - * @return string + * @param string|array ...$classList */ - protected function join(string|array ...$classList): string + private function join(string|array ...$classList): string { $output = []; @@ -181,4 +112,85 @@ protected function join(string|array ...$classList): string return implode(' ', $output); } + + private function determineClassContext(string $originalClassName): ClassContext + { + $modifiersContext = $this->classUtils->splitModifiers($originalClassName); + + $classGroupId = $this->classUtils->getClassGroupId( + is_null($modifiersContext->maybePostfixModifierPosition) + ? $modifiersContext->baseClassName + : substr($modifiersContext->baseClassName, 0, $modifiersContext->maybePostfixModifierPosition) + ); + + $hasPostfixModifier = is_int($modifiersContext->maybePostfixModifierPosition); + + if (!$classGroupId) { + if (is_null($modifiersContext->maybePostfixModifierPosition)) { + return new ClassContext( + isTailwindClass: false, + originalClassName: $originalClassName + ); + } + + $classGroupId = $this->classUtils->getClassGroupId($modifiersContext->baseClassName); + + if (!$classGroupId) { + return new ClassContext( + isTailwindClass: false, + originalClassName: $originalClassName + ); + } + + $hasPostfixModifier = false; + } + + $variantModifier = implode(':', $this->classUtils->sortModifiers($modifiersContext->modifiers)); + $modifierId = $modifiersContext->hasImportantModifier + ? $variantModifier . self::IMPORTANT_MODIFIER + : $variantModifier; + + return new ClassContext( + isTailwindClass: true, + originalClassName: $originalClassName, + hasPostfixModifier: $hasPostfixModifier, + modifierId: $modifierId, + classGroupId: $classGroupId, + ); + } + + private function filterConflictingClasses(array $classes): array + { + /** + * Set of classGroupIds in following format: + * `{importantModifier}{variantModifiers}{classGroupId}` + * + * @example 'float' + * @example 'hover:focus:bg-color' + * @example 'md:!pr' + * + * @var string[] $classGroupsInConflict + */ + $classGroupsInConflict = []; + + return array_filter($classes, function (ClassContext $context) use (&$classGroupsInConflict) { + if (!$context->isTailwindClass) { + return true; + } + + $classId = $context->modifierId . $context->classGroupId; + if (in_array($classId, $classGroupsInConflict)) { + return false; + } + + $classGroupsInConflict[] = $classId; + + $conflicts = $this->classUtils->getConflictingClassGroupIds((string) $context->classGroupId, $context->hasPostfixModifier); + foreach ($conflicts as $group) { + $classGroupsInConflict[] = $context->modifierId . $group; + } + + return true; + }); + } } diff --git a/src/TailwindMergeConfig.php b/src/TailwindMergeConfig.php index e1b2a2f..c23fbf2 100644 --- a/src/TailwindMergeConfig.php +++ b/src/TailwindMergeConfig.php @@ -27,9 +27,9 @@ final class TailwindMergeConfig * @param $cacheSize int Integer indicating size of LRU cache used for memoizing results. * - Cache might be up to twice as big as `cacheSize` * - No cache is used for values <= 0 - * @param $separator string Custom separator for modifiers in Tailwind classes + * @param $separator string Custom separator for modifiers in Tailwind classes. * See https://tailwindcss.com/docs/configuration#separator - * @param $prefix null|string Prefix added to Tailwind-generated classes + * @param $prefix null|string Prefix added to Tailwind-generated classes. * See https://tailwindcss.com/docs/configuration#prefix * @param $theme array Theme scales used in classGroups. * The keys are the same as in the Tailwind config but the values are sometimes defined more broadly. @@ -44,7 +44,10 @@ final class TailwindMergeConfig * The key is ID of class group which creates conflict, values are IDs of class groups which receive a conflict. * A class group is ID is the key of a class group in classGroups object. * Example : { gap: ['gap-x', 'gap-y'] } - * + * @param $conflictingClassGroupModifiers array Postfix modifiers conflicting with other class groups. + * A class group ID is the key of a class group in classGroups object. + * Example : { 'font-size': ['leading'] } + * @throws BadThemeException */ public function __construct( @@ -59,6 +62,25 @@ public function __construct( $this->validate(); } + + /** + * @param array $config + * @param bool $extend Determine if given values extends or replace the default configuration + */ + public static function fromArray(array $config, bool $extend = true): static + { + $output = static::default(); + + foreach($config as $key => $value) { + $methodName = lcfirst(str_replace('_', '', ucwords($key, '_'))); + if (method_exists($output, $methodName)) { + $output->{$methodName}($value, $extend); + } + } + + return $output; + } + /** * @throws BadThemeException */ diff --git a/src/helpers.php b/src/helpers.php deleted file mode 100644 index 0e363b7..0000000 --- a/src/helpers.php +++ /dev/null @@ -1,15 +0,0 @@ -|string ...$classLists - */ - function tw_merge(array|string ...$classLists): string - { - return (new TailwindMerge())->merge(...$classLists); - } -} diff --git a/tests/Laravel/LaravelTest.php b/tests/Laravel/LaravelTest.php new file mode 100644 index 0000000..5a5b486 --- /dev/null +++ b/tests/Laravel/LaravelTest.php @@ -0,0 +1,72 @@ +=')) { + test('Laravel is only used for Laravel integration') + ->expect('Illuminate') + ->toOnlyBeUsedIn('YieldStudio\\TailwindMerge\\Laravel'); +} + +it('tailwind merge is correctly bind on the container', function () { + $instance = app('tailwind-merge'); + + expect($instance)->toBeInstanceOf(TailwindMerge::class); + + expect(app('tailwind-merge'))->toBe($instance); + expect(app(TailwindMerge::class))->toBe($instance); +}); + +it('customize configuration with merge strategy', function () { + /* @phpstan-ignore-next-line */ + app()->bind('config', fn () => new Repository([ + 'tailwind-merge' => [ + 'class_groups' => [ + 'fooKey' => [[ + 'fooKey' => ['bar', 'baz']]], + ], + 'strategy' => 'merge', + ], + ])); + + /* @phpstan-ignore-next-line */ + expect(app('tailwind-merge')->merge('fooKey-bar fooKey-baz bg-red-100 bg-red-500'))->toBe('fooKey-baz bg-red-500'); +}); + +it('customize configuration with replace strategy', function () { + /* @phpstan-ignore-next-line */ + app()->bind('config', fn () => new Repository([ + 'tailwind-merge' => [ + 'class_groups' => [ + 'fooKey' => [[ + 'fooKey' => ['bar', 'baz']]], + ], + 'strategy' => 'replace', + ], + ])); + + /* @phpstan-ignore-next-line */ + expect(app('tailwind-merge')->merge('fooKey-bar fooKey-baz bg-red-100 bg-red-500'))->toBe('fooKey-baz bg-red-100 bg-red-500'); +}); + +it('tw helper works correctly', function () { + expect(tw('text-red-100 text-red-500'))->toBe('text-red-500'); +}); + +it('blade components attribute works correctly', function () { + Blade::component('avatar', Avatar::class); + + $this->blade('')->assertSee('
', false); +}); + + +it('blade directive works correctly', function () { + $this->blade('
')->assertSee('class="text-red-500"', false); +}); diff --git a/tests/Laravel/TestSupport/Avatar.php b/tests/Laravel/TestSupport/Avatar.php new file mode 100644 index 0000000..8b31c04 --- /dev/null +++ b/tests/Laravel/TestSupport/Avatar.php @@ -0,0 +1,17 @@ +tw('rounded-full') }}> + blade; + } +} diff --git a/tests/LaravelTestCase.php b/tests/LaravelTestCase.php new file mode 100644 index 0000000..5145395 --- /dev/null +++ b/tests/LaravelTestCase.php @@ -0,0 +1,23 @@ +in('Unit'); + +uses(LaravelTestCase::class)->in('Laravel'); diff --git a/tests/Unit/ClassMapTest.php b/tests/Unit/ClassMapTest.php index 10fde17..be56ac2 100644 --- a/tests/Unit/ClassMapTest.php +++ b/tests/Unit/ClassMapTest.php @@ -2,35 +2,41 @@ declare(strict_types=1); -use Illuminate\Support\Collection; use YieldStudio\TailwindMerge\ClassMapFactory; use YieldStudio\TailwindMerge\ClassPart; -use YieldStudio\TailwindMerge\ClassValidator; use YieldStudio\TailwindMerge\TailwindMergeConfig; function getClassGroupsInClassPart(ClassPart $classPart): array { - $classGroups = new Collection(); + $classGroups = []; if ($classPart->classGroupId) { - $classGroups->push($classPart->classGroupId); + $classGroups[] = $classPart->classGroupId; } - $classPart->validators->each(fn (ClassValidator $validator) => $classGroups->push($validator->classGroupId)); + foreach ($classPart->validators as $validator) { + $classGroups[] = $validator->classGroupId; + } + + foreach ($classPart->nextPart as $nextClassPart) { + $classGroups = array_merge($classGroups, getClassGroupsInClassPart($nextClassPart)); + } - $classPart->nextPart->each(function (ClassPart $nextClassPart) use ($classGroups) { - $classGroups->push(...getClassGroupsInClassPart($nextClassPart)); - }); + $classGroups = array_unique($classGroups); + sort($classGroups); - return $classGroups->unique()->sort()->values()->toArray(); + $classGroups = array_values($classGroups); + + return $classGroups; } test('class map has correct class groups at first part', function () { $classMap = ClassMapFactory::create(TailwindMergeConfig::default()); - $classGroupsByFirstPart = $classMap->nextPart - ->mapWithKeys(fn ($value, $key) => [$key => getClassGroupsInClassPart($value)]) - ->toArray(); + $classGroupsByFirstPart = []; + foreach ($classMap->nextPart as $key => $classPart) { + $classGroupsByFirstPart[$key] = getClassGroupsInClassPart($classPart); + } $this->assertNull($classMap->classGroupId); $this->assertCount(0, $classMap->validators); diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php deleted file mode 100644 index 2c82011..0000000 --- a/tests/Unit/HelpersTest.php +++ /dev/null @@ -1,7 +0,0 @@ -toBe('hidden'); -});