Skip to content

Commit

Permalink
add blade component support and fix livewire support
Browse files Browse the repository at this point in the history
  • Loading branch information
mozex committed Feb 27, 2024
1 parent 88b116e commit 83ab14f
Show file tree
Hide file tree
Showing 33 changed files with 383 additions and 9 deletions.
6 changes: 6 additions & 0 deletions config/modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@
'*/Resources/views',
],
],
AssetType::BladeComponents->value => [
'active' => true,
'patterns' => [
'*/View/Components',
],
],
AssetType::Routes->value => [
'active' => true,
'patterns' => [
Expand Down
3 changes: 3 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
<clover outputFile="build/logs/clover.xml"/>
</report>
</coverage>
<php>
<env name="APP_KEY" value="base64:Ftfzwdew7YHs1KzQvIOAoLAV/0VajVmSYPlUEmJm95A="/>
</php>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
Expand Down
3 changes: 3 additions & 0 deletions src/Enums/AssetType.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Mozex\Modules\Enums;

use Mozex\Modules\Contracts\BaseScout;
use Mozex\Modules\Scouts\BladeComponentsScout;
use Mozex\Modules\Scouts\CommandsScout;
use Mozex\Modules\Scouts\ConfigsScout;
use Mozex\Modules\Scouts\HelpersScout;
Expand All @@ -27,6 +28,7 @@ enum AssetType: string
case Schedules = 'schedules';
case Configs = 'configs';
case Views = 'views';
case BladeComponents = 'blade-components';
case Routes = 'routes';
case LivewireComponents = 'livewire-components';
case NovaResources = 'nova-resources';
Expand All @@ -46,6 +48,7 @@ public function scout(): ?BaseScout
self::Schedules => SchedulesScout::create(),
self::Configs => ConfigsScout::create(),
self::Views => ViewsScout::create(),
self::BladeComponents => BladeComponentsScout::create(),
self::Routes => RoutesScout::create(),
self::LivewireComponents => LivewireComponentsScout::create(),
self::NovaResources => NovaResourcesScout::create(),
Expand Down
62 changes: 61 additions & 1 deletion src/ModulesServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Contracts\Auth\Access\Gate as GateInstance;
use Illuminate\Contracts\Foundation\CachesConfiguration;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Route;
Expand Down Expand Up @@ -38,6 +39,7 @@ public function packageBooted(): void
$this->bootTranslations();
$this->bootConfigs();
$this->bootViews();
$this->bootBladeComponents();
$this->bootModels();
$this->bootFactories();
$this->bootPolicies();
Expand Down Expand Up @@ -137,6 +139,25 @@ protected function bootViews(): void
});
}

protected function bootBladeComponents(): void
{
if (AssetType::BladeComponents->isDeactive()) {
return;
}

AssetType::BladeComponents->scout()->collect()
->each(function (array $asset): void {
Blade::component(
class: $asset['namespace'],
alias: sprintf(
'%s::%s',
strtolower($asset['module']),
$this->getViewName($asset, AssetType::BladeComponents)
)
);
});
}

protected function bootModels(): void
{
if (AssetType::Models->isDeactive()) {
Expand Down Expand Up @@ -248,6 +269,10 @@ protected function bootPolicies(): void

protected function bootRoutes(): void
{
if ($this->app->routesAreCached()) {
return;
}

if (AssetType::Routes->isDeactive()) {
return;
}
Expand Down Expand Up @@ -289,7 +314,7 @@ protected function bootLivewire(): void
AssetType::LivewireComponents->scout()->collect()
->each(function (array $asset): void {
Livewire::component(
str(class_basename($asset['namespace']))->kebab()->toString(),
$this->getViewName($asset, AssetType::LivewireComponents),
$asset['namespace']
);
});
Expand Down Expand Up @@ -337,4 +362,39 @@ protected function registerServicePorviders(): void
$this->app->register($asset['namespace']);
});
}

protected function getViewName(array $asset, AssetType $type): string
{
foreach ($type->patterns() as $pattern) {
$sub = str(realpath($asset['path']))
->replaceFirst(realpath(Modules::modulesPath()), '')
->replace('\\', '/')
->replaceFirst('/', '')
->replaceMatches(
'/'.str($pattern)
->replaceFirst('*', '.*?')
->replace('/', '\/').'\//',
''
)
->replaceLast('.php', '')
->explode('/')
->filter();

if ($sub->first() === $asset['module'] && $sub->count() > 1) {
continue;
}

return $sub
->map(
fn (string $view) => str($view)
->replaceMatches('/(?<! )[A-Z]/', '-$0')
->replaceFirst('-', '')
->lower()
->toString()
)
->implode('.');
}

return strtolower(class_basename($asset['namespace']));
}
}
29 changes: 29 additions & 0 deletions src/Scouts/BladeComponentsScout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Mozex\Modules\Scouts;

use Illuminate\View\Component;
use Mozex\Modules\Contracts\ModuleClassScout;
use Mozex\Modules\Enums\AssetType;
use Spatie\StructureDiscoverer\Data\DiscoveredClass;
use Spatie\StructureDiscoverer\Discover;
use Spatie\StructureDiscoverer\Enums\Sort;

class BladeComponentsScout extends ModuleClassScout
{
public function asset(): AssetType
{
return AssetType::BladeComponents;
}

protected function definition(): Discover
{
return Discover::in(...$this->patterns())
->parallel()
->classes()
->extending(Component::class)
->custom(fn (DiscoveredClass $structure) => ! $structure->isAbstract)
->full()
->sortBy(Sort::Name);
}
}
19 changes: 19 additions & 0 deletions testbench
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env php
<?php

define('TESTBENCH_CORE', true);
define('TESTBENCH_WORKING_PATH', $workingPath = getcwd());

require $_composer_autoload_path ?? __DIR__.'/vendor/autoload.php';

$config = Orchestra\Testbench\Foundation\Config::loadFromYaml(
workingPath: $workingPath,
defaults: [
'providers' => [],
'dont-discover' => [],
],
);

$commander = new Orchestra\Testbench\Console\Commander($config, $workingPath);

$commander->handle();
66 changes: 66 additions & 0 deletions tests/Feature/BladeComponentsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

use Illuminate\Support\Facades\Blade;
use Modules\First\View\Components\Filter;
use Modules\First\View\Components\WrongComponent;
use Modules\Second\View\Components\Button\Loading;
use Modules\Second\View\Components\Search;
use Mozex\Modules\Enums\AssetType;
use Mozex\Modules\Scouts\BladeComponentsScout;

test('scout will not collect when disabled', function () {
config()->set(
'modules.'.AssetType::BladeComponents->value.'.active',
false
);

expect(BladeComponentsScout::create()->get())->toHaveCount(0);
});

test('scout has correct structure', function () {
expect(BladeComponentsScout::create()->get())
->each->toHaveKeys(['module', 'path', 'namespace']);
});

test('scout will select correct classes', function () {
expect(BladeComponentsScout::create()->collect()->pluck('namespace'))
->toContain(Filter::class)
->toContain(Search::class)
->toContain(Loading::class)
->not->toContain(WrongComponent::class);
});

it('can load blade components', function () {
$components = Blade::getClassComponentAliases();

BladeComponentsScout::create()->collect()
->each(function (array $asset) use ($components) {
expect($components)->toContain($asset['namespace']);
});

expect(Blade::render(
string: '<x-first::filter name="Filter"/>',
deleteCachedView: true
))
->toContain('Filter Component')
->and(Blade::render(
string: '<x-first::select name="Select"/>',
deleteCachedView: true
))
->toContain('Select Component')
->and(Blade::render(
string: '<x-first::without-view name="Without View"/>',
deleteCachedView: true
))
->toContain('Without View Component')
->and(Blade::render(
string: '<x-second::search name="Search"/>',
deleteCachedView: true
))
->toContain('Search Component')
->and(Blade::render(
string: '<x-second::button.loading name="Loading"/>',
deleteCachedView: true
))
->toContain('Loading Component');
});
16 changes: 16 additions & 0 deletions tests/Feature/LivewireComponentsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,20 @@
->each(function (array $asset) use ($components) {
expect($components->getName($asset['namespace']))->not->toBeNull();
});

expect(Blade::render(
string: '<livewire:teams/>',
deleteCachedView: true
))
->toContain('Teams Livewire Component')
->and(Blade::render(
string: '<livewire:list-users/>',
deleteCachedView: true
))
->toContain('List Users Livewire Component')
->and(Blade::render(
string: '<livewire:nested.nested-users/>',
deleteCachedView: true
))
->toContain('Nested Users Livewire Component');
});
23 changes: 23 additions & 0 deletions tests/Feature/ViewsTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Illuminate\Support\Facades\Blade;
use Mozex\Modules\Enums\AssetType;
use Mozex\Modules\Facades\Modules;
use Mozex\Modules\Scouts\ViewsScout;
Expand Down Expand Up @@ -33,4 +34,26 @@
expect($views)->toHaveKey(strtolower($asset['module']))
->and($views[strtolower($asset['module'])])->toHaveCount(1)->toContain($asset['path']);
});

expect(view('first::first')->render())
->toContain('First Page')
->and(view('second::second')->render())
->toContain('Second Page')
->and(view('second::pages.page')->render())
->toContain('Nested Page')
->and(Blade::render(
string: '<x-first::input/>',
deleteCachedView: true
))
->toContain('Input Component')
->and(Blade::render(
string: '<x-second::checkbox/>',
deleteCachedView: true
))
->toContain('Checkbox Component')
->and(Blade::render(
string: '<x-second::button.submit/>',
deleteCachedView: true
))
->toContain('Submit Component');
});
2 changes: 0 additions & 2 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

namespace Mozex\Modules\Tests;

use Illuminate\Foundation\Testing\Concerns\InteractsWithViews;
use Orchestra\Testbench\Concerns\WithWorkbench;
use Orchestra\Testbench\TestCase as Orchestra;

class TestCase extends Orchestra
{
use InteractsWithViews;
use WithWorkbench;
}
20 changes: 20 additions & 0 deletions workbench/Modules/First/Components/Select.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Modules\First\Components;

use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class Select extends Component
{
public function __construct(
public string $name,
) {
$this->name .= ' Component';
}

public function render(): View
{
return view('first::components.select');
}
}
21 changes: 21 additions & 0 deletions workbench/Modules/First/Components/WithoutView.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Modules\First\Components;

use Illuminate\View\Component;

class WithoutView extends Component
{
public function __construct(
public string $name,
) {
$this->name .= ' Component';
}

public function render(): string
{
return <<<'blade'
{{ $name }}
blade;
}
}
16 changes: 16 additions & 0 deletions workbench/Modules/First/Livewire/Nested/NestedUsers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Modules\First\Livewire\Nested;

use Illuminate\View\View;
use Livewire\Component;

class NestedUsers extends Component
{
public function render(): View
{
return view('first::livewire.nested.nested-users', [
'name' => 'Nested Users Livewire Component',
]);
}
}
Loading

0 comments on commit 83ab14f

Please sign in to comment.