diff --git a/app/Classes/FilamentInput.php b/app/Classes/FilamentInput.php index a40e58f20..cac559a60 100644 --- a/app/Classes/FilamentInput.php +++ b/app/Classes/FilamentInput.php @@ -96,19 +96,26 @@ class FilamentInput ->disabled($setting->disabled ?? false) ->hint($setting->hint ?? '') ->hintColor('primary') - ->helperText($setting->description ?? ''); + ->helperText($setting->description ?? '') + ->live(onBlur: true); - // For required TagsInput, validate that array has at least one item - // Standard 'required' rule considers empty arrays as valid + // For required TagsInput, add custom validation if ($setting->required ?? false) { - $input->required() - ->rule(function () { + $input->rules([ + function () { return function (string $attribute, $value, \Closure $fail) { - if (!is_array($value) || empty($value)) { - $fail('The :attribute field must have at least 1 items.'); + // Check if value is an array and has at least one item + if (!is_array($value)) { + $fail('The :attribute field is required.'); + return; + } + if (count($value) === 0) { + $fail('The :attribute field is required.'); + return; } }; - }); + } + ]); } return $input; diff --git a/extensions/Servers/ManualServers/Admin/Pages/ManualServersManage.php b/extensions/Servers/ManualServers/Admin/Pages/ManualServersManage.php index e2ccf02f1..49f5ecea1 100644 --- a/extensions/Servers/ManualServers/Admin/Pages/ManualServersManage.php +++ b/extensions/Servers/ManualServers/Admin/Pages/ManualServersManage.php @@ -12,8 +12,8 @@ use Paymenter\Extensions\Servers\ManualServers\ManualServers; class ManualServersManage extends Page implements HasForms { use InteractsWithForms; - protected static string|\BackedEnum|null $navigationIcon = 'ri-hard-drive-3-line'; - protected string $view = 'manual-servers::admin.pages.manual-servers-manage'; + protected static ?string $navigationIcon = 'ri-hard-drive-3-line'; + protected static string $view = 'manual-servers::admin.pages.manual-servers-manage'; protected static bool $shouldRegisterNavigation = false; protected static ?string $slug = 'manual-servers-manage/{service}'; public ?Service $service = null; diff --git a/extensions/Servers/ManualServers/Admin/Pages/ManualServersQueue.php b/extensions/Servers/ManualServers/Admin/Pages/ManualServersQueue.php index 4e7962f4b..a21c86972 100644 --- a/extensions/Servers/ManualServers/Admin/Pages/ManualServersQueue.php +++ b/extensions/Servers/ManualServers/Admin/Pages/ManualServersQueue.php @@ -13,9 +13,9 @@ class ManualServersQueue extends Page implements HasTable { use InteractsWithTable; - protected static string|\BackedEnum|null $navigationIcon = 'ri-hard-drive-3-line'; - protected string $view = 'manual-servers::admin.pages.manual-servers-queue'; - protected static string|\UnitEnum|null $navigationGroup = 'Extensions'; + protected static ?string $navigationIcon = 'ri-hard-drive-3-line'; + protected static string $view = 'manual-servers::admin.pages.manual-servers-queue'; + protected static ?string $navigationGroup = 'Extensions'; protected static ?string $navigationLabel = 'Manual Servers'; protected static ?int $navigationSort = 100; diff --git a/extensions/Servers/ManualServers/ManualServers.php b/extensions/Servers/ManualServers/ManualServers.php index ff40873a1..158b9112f 100644 --- a/extensions/Servers/ManualServers/ManualServers.php +++ b/extensions/Servers/ManualServers/ManualServers.php @@ -162,6 +162,70 @@ class ManualServers extends Server return $config; } + + /** + * Get configuration options for checkout (user-provided data during purchase) + * + * @param Product $product + * @return array + */ + public function getCheckoutConfig($product = null): array + { + if (!$product) { + return []; + } + + // Create a temporary service object to use getCredentialFieldsForService + $tempService = new Service(['product_id' => $product->id]); + $tempService->setRelation('product', $product); + + $fields = $this->getCredentialFieldsForService($tempService); + + // Convert to checkout config format + $checkoutFields = []; + foreach ($fields as $field) { + $checkoutFields[] = [ + 'name' => $field['name'], + 'type' => $field['type'], + 'label' => $field['label'], + 'required' => true, + 'placeholder' => 'Enter ' . strtolower($field['label']), + ]; + } + + return $checkoutFields; + } + + /** + * Get configuration options for admin when managing a service + * This allows admins to enter credentials and other service-specific data + * + * @param Service $service + * @return array + */ + public function getUserConfig(Service $service): array + { + $fields = $this->getCredentialFieldsForService($service); + + // Get existing credentials + $credentialsJson = $service->properties->where('key', 'credentials_data')->first()?->value ?? '[]'; + $existingCredentials = json_decode($credentialsJson, true) ?? []; + + // Format fields for the admin interface + $configFields = []; + foreach ($fields as $field) { + $configFields[] = [ + 'name' => $field['name'], + 'type' => $field['type'], + 'label' => $field['label'], + 'required' => false, + 'default' => $existingCredentials[$field['name']] ?? '', + ]; + } + + return $configFields; + } + public function createServer(Service $service, $params = []): bool { $processes = $this->getProcessesForService($service); @@ -181,9 +245,20 @@ class ManualServers extends Server ['key' => 'processes_data'], ['value' => json_encode($processData)] ); + + // Extract credential values from params (checkout form data) + $credentialFields = $this->getCredentialFieldsForService($service); + $credentials = []; + foreach ($credentialFields as $field) { + $fieldName = $field['name']; + if (isset($params[$fieldName])) { + $credentials[$fieldName] = $params[$fieldName]; + } + } + $service->properties()->updateOrCreate( ['key' => 'credentials_data'], - ['value' => json_encode([])] + ['value' => json_encode($credentials)] ); $service->properties()->updateOrCreate( ['key' => 'deployed_at'], @@ -370,13 +445,30 @@ class ManualServers extends Server $credentialsJson = $service->properties->where('key', 'credentials_data')->first()?->value ?? '[]'; $deployedAt = $service->properties->where('key', 'deployed_at')->first()?->value; $feedback = $service->properties->where('key', 'feedback')->first()?->value ?? ''; + + // Read credentials from individual properties + $credentialFields = $this->getCredentialFieldsForService($service); + $credentials = []; + foreach ($credentialFields as $field) { + $fieldName = $field['name']; + $value = $service->properties->where('key', $fieldName)->first()?->value; + if ($value !== null) { + $credentials[$fieldName] = $value; + } + } + + // Fallback to credentials_data JSON if individual properties are empty + if (empty($credentials)) { + $credentials = json_decode($credentialsJson, true) ?? []; + } + return [ 'status' => $status, 'processes' => json_decode($processesJson, true) ?? [], - 'credentials' => json_decode($credentialsJson, true) ?? [], + 'credentials' => $credentials, 'deployed_at' => $deployedAt, 'feedback' => $feedback, - 'credential_fields' => $this->getCredentialFieldsForService($service), + 'credential_fields' => $credentialFields, 'buttons' => $this->getButtonsForService($service), 'instructions' => $service->product->settings->where('key', 'instructions')->first()?->value ?? '', ]; diff --git a/extensions/Servers/ManualServers/resources/views/admin/pages/manual-servers-manage.blade.php b/extensions/Servers/ManualServers/resources/views/admin/pages/manual-servers-manage.blade.php index 6a12871b0..8d67d00c3 100644 --- a/extensions/Servers/ManualServers/resources/views/admin/pages/manual-servers-manage.blade.php +++ b/extensions/Servers/ManualServers/resources/views/admin/pages/manual-servers-manage.blade.php @@ -65,6 +65,52 @@ + @if(!empty($this->credentialData)) + + User-Provided Information + + Information entered by the client during checkout + +
+ @foreach($this->credentialData as $key => $value) + @php + $fieldConfig = collect($this->credentialFields)->where('name', $key)->first(); + $label = $fieldConfig['label'] ?? ucfirst(str_replace('_', ' ', $key)); + $isPassword = ($fieldConfig['type'] ?? 'text') === 'password'; + @endphp +
+

{{ $label }}

+ +
+
+ @if($isPassword) +

+ •••••••• + {{ $value }} + + Show + Hide + +

+ @else +

{{ $value ?: 'N/A' }}

+ @endif +
+ + + Copy + +
+
+ @endforeach +
+
+ @endif + @if(empty($this->processData)) Initialize Setup Flow diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore deleted file mode 100644 index 01e4a6cda..000000000 --- a/storage/framework/cache/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!data/ -!.gitignore diff --git a/vendor/_laravel_ide/discover-cfae44312a91110c691459b4106ded61.php b/vendor/_laravel_ide/discover-cfae44312a91110c691459b4106ded61.php new file mode 100644 index 000000000..6b4c8dfad --- /dev/null +++ b/vendor/_laravel_ide/discover-cfae44312a91110c691459b4106ded61.php @@ -0,0 +1,440 @@ +getMessage()); + } +} + +try { + $app = require_once __DIR__ . '/../../bootstrap/app.php'; +} catch (\Throwable $e) { + LaravelVsCode::startupError($e); + exit(1); +} + +$app->register(new class($app) extends \Illuminate\Support\ServiceProvider +{ + public function boot() + { + config([ + 'logging.channels.null' => [ + 'driver' => 'monolog', + 'handler' => \Monolog\Handler\NullHandler::class, + ], + 'logging.default' => 'null', + ]); + } +}); + +try { + $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class); + $kernel->bootstrap(); +} catch (\Throwable $e) { + LaravelVsCode::startupError($e); + exit(1); +} + +echo LaravelVsCode::outputMarker('START_OUTPUT'); + +$components = new class { + protected $autoloaded = []; + + protected $prefixes = []; + + public function __construct() + { + $this->autoloaded = require base_path("vendor/composer/autoload_psr4.php"); + } + + public function all() + { + $components = collect(array_merge( + $this->getStandardClasses(), + $this->getStandardViews(), + $this->getNamespaced(), + $this->getAnonymousNamespaced(), + $this->getAnonymous(), + $this->getAliases(), + $this->getVendorComponents(), + ))->groupBy('key')->pipe($this->setProps(...))->map(fn($items) => [ + 'isVendor' => $items->first()['isVendor'], + 'paths' => $items->pluck('path')->unique()->values(), + 'props' => $this->formatProps($items), + ]); + + return [ + 'components' => $components, + 'prefixes' => $this->prefixes, + ]; + } + + protected function formatProps($items) + { + $props = $items->pluck('props'); + + if ($codeBlock = $props->firstWhere(fn ($prop) => is_string($prop))) { + return $codeBlock; + } + + return $props->values()->filter()->flatMap(fn($i) => $i); + } + + protected function getStandardViews() + { + $path = resource_path('views/components'); + + return $this->findFiles($path, 'blade.php'); + } + + protected function findFiles($path, $extension, $keyCallback = null) + { + if (!is_dir($path)) { + return []; + } + + $files = \Symfony\Component\Finder\Finder::create() + ->files() + ->name("*." . $extension) + ->in($path); + $components = []; + $pathRealPath = realpath($path); + + foreach ($files as $file) { + $realPath = $file->getRealPath(); + + $key = str($realPath) + ->replace($pathRealPath, '') + ->ltrim('/\\') + ->replace('.' . $extension, '') + ->replace(['/', '\\'], '.') + ->pipe(fn($str) => $this->handleIndexComponents($str)); + + $components[] = [ + "path" => LaravelVsCode::relativePath($realPath), + "isVendor" => LaravelVsCode::isVendor($realPath), + "key" => $keyCallback ? $keyCallback($key) : $key, + ]; + } + + return $components; + } + + protected function getStandardClasses() + { + $path = app_path('View/Components'); + + $appNamespace = collect($this->autoloaded) + ->filter(fn($paths) => in_array(app_path(), $paths)) + ->keys() + ->first() ?? ''; + + return collect($this->findFiles( + $path, + 'php', + fn($key) => $key->explode('.') + ->map(fn($p) => \Illuminate\Support\Str::kebab($p)) + ->implode('.'), + ))->map(function ($item) use ($appNamespace) { + $class = str($item['path']) + ->after('View/Components/') + ->replace('.php', '') + ->replace('/', '\\') + ->prepend($appNamespace . 'View\\Components\\') + ->toString(); + + if (!class_exists($class)) { + return $item; + } + + $reflection = new \ReflectionClass($class); + $parameters = collect($reflection->getConstructor()?->getParameters() ?? []) + ->filter(fn($p) => $p->isPromoted()) + ->flatMap(fn($p) => [$p->getName() => $p->isOptional() ? $p->getDefaultValue() : null]) + ->all(); + + $props = collect($reflection->getProperties()) + ->filter(fn($p) => $p->isPublic() && $p->getDeclaringClass()->getName() === $class) + ->map(fn($p) => [ + 'name' => \Illuminate\Support\Str::kebab($p->getName()), + 'type' => (string) ($p->getType() ?? 'mixed'), + 'default' => $p->getDefaultValue() ?? $parameters[$p->getName()] ?? null, + ]); + + [$except, $props] = $props->partition(fn($p) => $p['name'] === 'except'); + + if ($except->isNotEmpty()) { + $except = $except->first()['default']; + $props = $props->reject(fn($p) => in_array($p['name'], $except)); + } + + return [ + ...$item, + 'props' => $props, + ]; + })->all(); + } + + protected function getAliases() + { + $components = []; + + foreach (\Illuminate\Support\Facades\Blade::getClassComponentAliases() as $key => $class) { + if (class_exists($class)) { + $reflection = new ReflectionClass($class); + + $components[] = [ + "path" => LaravelVsCode::relativePath($reflection->getFileName()), + "isVendor" => LaravelVsCode::isVendor($reflection->getFileName()), + "key" => $key, + ]; + } + } + + return $components; + } + + protected function getAnonymousNamespaced() + { + $components = []; + + foreach (\Illuminate\Support\Facades\Blade::getAnonymousComponentNamespaces() as $key => $dir) { + $path = collect([$dir, resource_path('views/' . $dir)])->first(fn($p) => is_dir($p)); + + if (!$path) { + continue; + } + + array_push( + $components, + ...$this->findFiles( + $path, + 'blade.php', + fn($k) => $k->kebab()->prepend($key . "::"), + ) + ); + } + + return $components; + } + + protected function getAnonymous() + { + $components = []; + + foreach (\Illuminate\Support\Facades\Blade::getAnonymousComponentPaths() as $item) { + array_push( + $components, + ...$this->findFiles( + $item['path'], + 'blade.php', + function (\Illuminate\Support\Stringable $key) use ($item) { + $prefix = $item['prefix'] ? $item['prefix'] . '::' : ''; + $key = $key->kebab(); + $keys = []; + + $keys[] = $key->prepend($prefix); + + if ($item['prefix'] === 'flux') { + $keys[] = $key->prepend('flux:'); + } + + return $keys; + }, + ) + ); + + if (!in_array($item['prefix'], $this->prefixes)) { + $this->prefixes[] = $item['prefix']; + } + } + + return $components; + } + + protected function getVendorComponents(): array + { + $components = []; + + /** @var \Illuminate\View\Factory $view */ + $view = \Illuminate\Support\Facades\App::make('view'); + + /** @var \Illuminate\View\FileViewFinder $finder */ + $finder = $view->getFinder(); + + /** @var array> $views */ + $views = $finder->getHints(); + + foreach ($views as $key => $paths) { + foreach ($paths as $path) { + $path .= '/components'; + + if (!is_dir($path)) { + continue; + } + + array_push( + $components, + ...$this->findFiles( + $path, + 'blade.php', + fn (\Illuminate\Support\Stringable $k) => $k->kebab()->prepend($key.'::'), + ) + ); + } + } + + return $components; + } + + protected function handleIndexComponents($str) + { + if ($str->endsWith('.index')) { + return $str->replaceLast('.index', ''); + } + + if (!$str->contains('.')) { + return $str; + } + + $parts = $str->explode('.'); + + if ($parts->slice(-2)->unique()->count() === 1) { + $parts->pop(); + + return str($parts->implode('.')); + } + + return $str; + } + + protected function getNamespaced() + { + $namespaced = \Illuminate\Support\Facades\Blade::getClassComponentNamespaces(); + $components = []; + + foreach ($namespaced as $key => $classNamespace) { + $path = $this->getNamespacePath($classNamespace); + + if (!$path) { + continue; + } + + array_push( + $components, + ...$this->findFiles( + $path, + 'php', + fn($k) => $k->kebab()->prepend($key . "::"), + ) + ); + } + + return $components; + } + + protected function getNamespacePath($classNamespace) + { + foreach ($this->autoloaded as $ns => $paths) { + if (!str_starts_with($classNamespace, $ns)) { + continue; + } + + foreach ($paths as $p) { + $dir = str($classNamespace) + ->replace($ns, '') + ->replace('\\', '/') + ->prepend($p . DIRECTORY_SEPARATOR) + ->toString(); + + if (is_dir($dir)) { + return $dir; + } + } + + return null; + } + + return null; + } + + protected function setProps($groups) + { + try { + $compiler = app('blade.compiler'); + } catch (\Throwable $e) { + return $groups; + } + + return $groups->map(function ($group) use ($compiler) { + return $group->transform(function ($component) use ($compiler) { + if (isset($component['props'])) { + return $component; + } + + if (!str($component['path'])->endsWith('.blade.php')) { + return $component; + } + + if (! $props = $this->parseProps($compiler, $component)) { + return $component; + } + + return array_merge($component, ['props' => $props]); + }); + }); + } + + protected function parseProps($compiler, array $component): ?string + { + $content = file_get_contents(base_path($component['path'])); + + $result = ''; + + $compiler->directive('props', function ($expression) use (&$result) { + return $result = $expression; + }); + + $compiler->compileString($content); + + if (empty($result)) { + return null; + } + + return '@props('.$result.')'; + } +}; + +echo json_encode($components->all()); + +echo LaravelVsCode::outputMarker('END_OUTPUT'); + +exit(0); diff --git a/vendor/_laravel_ide/discover-ec2e9975ebf5755f76a162b3dce99361.php b/vendor/_laravel_ide/discover-ec2e9975ebf5755f76a162b3dce99361.php new file mode 100644 index 000000000..79ed3de8a --- /dev/null +++ b/vendor/_laravel_ide/discover-ec2e9975ebf5755f76a162b3dce99361.php @@ -0,0 +1,350 @@ +getMessage()); + } +} + +try { + $app = require_once __DIR__ . '/../../bootstrap/app.php'; +} catch (\Throwable $e) { + LaravelVsCode::startupError($e); + exit(1); +} + +$app->register(new class($app) extends \Illuminate\Support\ServiceProvider +{ + public function boot() + { + config([ + 'logging.channels.null' => [ + 'driver' => 'monolog', + 'handler' => \Monolog\Handler\NullHandler::class, + ], + 'logging.default' => 'null', + ]); + } +}); + +try { + $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class); + $kernel->bootstrap(); +} catch (\Throwable $e) { + LaravelVsCode::startupError($e); + exit(1); +} + +echo LaravelVsCode::outputMarker('START_OUTPUT'); + +$livewire = new class { + protected $namespaces; + protected $paths; + protected $extensions = [".blade.php", ".php", ".js", ".global.css", ".css", ".test.php"]; + + public function __construct() + { + $this->namespaces = collect( + config("livewire.component_namespaces", []) + )->map(LaravelVsCode::relativePath(...)); + + $this->paths = collect($this->namespaces->values()) + ->merge(config("livewire.component_locations", [])) + ->unique() + ->map(LaravelVsCode::relativePath(...)); + } + + public function parse(\Illuminate\Support\Collection $views) + { + return $this->isVersionFour() + ? $this->parseLivewireFour($views) + : $this->parseLivewireThree($views); + } + + protected function parseLivewireFour(\Illuminate\Support\Collection $views) + { + return $views + ->map(function (array $view) { + if (!$this->pathExists($view["path"])) { + return $view; + } + + if (is_null($component = $this->getComponent($key = $this->key($view)))) { + return $view; + } + + $files = $this->files($view); + + if (count($files) === 1 && !str($view['path'])->endsWith('.blade.php')) { + return null; + } + + return array_merge($view, [ + "key" => $key, + 'livewire' => [ + 'props' => $this->getProps($component), + 'files' => $files, + ], + ]); + }) + ->whereNotNull() + ->unique('key') + ->values(); + } + + protected function parseLivewireThree(\Illuminate\Support\Collection $views) + { + return $views->map(function (array $view) { + if (!str($view['key'])->startsWith('livewire.')) { + return $view; + } + + $key = str($view['key'])->after('livewire.')->value(); + + if (is_null($component = $this->getComponent($key))) { + return $view; + } + + return array_merge($view, [ + 'livewire' => [ + 'props' => $this->getProps($component), + 'files' => [$view['path']], + ], + ]); + }); + } + + protected function isVersionFour(): bool + { + return property_exists(\Livewire\LivewireManager::class, "v4") && + \Livewire\LivewireManager::$v4; + } + + protected function pathExists(string $path): bool + { + return $this->paths->contains(fn (string $item) => str($path)->contains($item)); + } + + protected function getComponent(string $key) + { + try { + return app("livewire")->new($key); + } catch (\Throwable $e) { + return null; + } + } + + protected function getProps($component): array + { + return array_map(function ($prop) use ($component) { + $reflection = new \ReflectionProperty($component, $prop); + + return [ + 'name' => $prop, + 'type' => (string) $reflection->getType() ?: 'mixed', + 'hasDefaultValue' => $reflection->hasDefaultValue(), + 'defaultValue' => $this->formatDefaultValue($reflection->getDefaultValue()), + ]; + }, array_keys($component->all())); + } + + protected function formatDefaultValue(mixed $value) + { + return is_string($value) ? "'{$value}'": $value; + } + + protected function key(array $view): string + { + return str($view["key"]) + ->replace("⚡", "") + ->when($this->isMfc($view), fn ($key) => $key->beforeLast(".")) + ->value(); + } + + protected function files(array $view): array + { + if (! $this->isMfc($view)) { + return [$view['path']]; + } + + $filePathWithoutExtension = str($view["path"])->replace($this->extensions, ""); + + return collect($this->extensions) + ->map(fn (string $extension) => $filePathWithoutExtension->append($extension)) + ->filter(fn (string $path) => \Illuminate\Support\Facades\File::exists($path)) + ->all(); + } + + protected function isMfc(array $view): bool + { + $directory = str($view["path"]) + ->replace("⚡", "") + ->dirname() + ->afterLast(DIRECTORY_SEPARATOR); + + $file = str($view["path"]) + ->replace("⚡", "") + ->basename() + ->replace($this->extensions, ""); + + $class = str($view["path"]) + ->dirname() + ->append(DIRECTORY_SEPARATOR . $file . ".php"); + + return $directory->is($file) + && \Illuminate\Support\Facades\File::exists($class); + } +}; + +$blade = new class ($livewire) { + public function __construct(protected $livewire) + { + // + } + + public function getAllViews() + { + $finder = app("view")->getFinder(); + + $paths = collect($finder->getPaths())->flatMap(fn($path) => $this->findViews($path)); + + $hints = collect($finder->getHints()) + ->filter( + fn ($_, $key) => ! (strlen($key) === 32 && ctype_xdigit($key)) + )->flatMap( + fn($paths, $key) => collect($paths)->flatMap( + fn($path) => collect($this->findViews($path))->map( + fn($value) => array_merge($value, ["key" => "{$key}::{$value["key"]}"]) + ) + ) + ); + + [$local, $vendor] = $paths + ->merge($hints) + ->values() + ->partition(fn($v) => !$v["isVendor"]); + + return $local + ->sortBy("key", SORT_NATURAL) + ->merge($vendor->sortBy("key", SORT_NATURAL)) + ->pipe($this->livewire->parse(...)); + } + + public function getAllComponents() + { + $namespaced = \Illuminate\Support\Facades\Blade::getClassComponentNamespaces(); + $autoloaded = require base_path("vendor/composer/autoload_psr4.php"); + $components = []; + + foreach ($namespaced as $key => $ns) { + $path = null; + + foreach ($autoloaded as $namespace => $paths) { + if (str_starts_with($ns, $namespace)) { + foreach ($paths as $p) { + $test = str($ns)->replace($namespace, '')->replace('\\', '/')->prepend($p . DIRECTORY_SEPARATOR)->toString(); + + if (is_dir($test)) { + $path = $test; + break; + } + } + + break; + } + } + + if (!$path) { + continue; + } + + $files = \Symfony\Component\Finder\Finder::create() + ->files() + ->name("*.php") + ->in($path); + + foreach ($files as $file) { + $realPath = $file->getRealPath(); + + $components[] = [ + "path" => LaravelVsCode::relativePath($realPath), + "isVendor" => str_contains($realPath, base_path("vendor")), + "key" => str($realPath) + ->replace(realpath($path), "") + ->replace(".php", "") + ->ltrim(DIRECTORY_SEPARATOR) + ->replace(DIRECTORY_SEPARATOR, ".") + ->kebab() + ->prepend($key . "::"), + ]; + } + } + + return $components; + } + + protected function findViews($path) + { + $paths = []; + + if (!is_dir($path)) { + return $paths; + } + + $finder = app("view")->getFinder(); + $extensions = array_map(fn($extension) => ".{$extension}", $finder->getExtensions()); + + $files = \Symfony\Component\Finder\Finder::create() + ->files() + ->name(array_map(fn ($ext) => "*{$ext}", $extensions)) + ->in($path); + + foreach ($files as $file) { + $paths[] = [ + "path" => LaravelVsCode::relativePath($file->getRealPath()), + "isVendor" => str_contains($file->getRealPath(), base_path("vendor")), + "key" => str($file->getRealPath()) + ->replace(realpath($path), "") + ->replace($extensions, "") + ->ltrim(DIRECTORY_SEPARATOR) + ->replace(DIRECTORY_SEPARATOR, "."), + ]; + } + + return $paths; + } +}; + +echo json_encode($blade->getAllViews()->merge($blade->getAllComponents())); + +echo LaravelVsCode::outputMarker('END_OUTPUT'); + +exit(0);