├── .gitignore ├── .gitattributes ├── CHANGELOG.md ├── src ├── config │ └── livewire-doctor.php ├── Facades │ └── LivewireDoctor.php ├── Commands │ └── LivewireDoctorCommand.php ├── LivewireDoctor.php └── LivewireDoctorServiceProvider.php ├── .github └── ISSUE_TEMPLATE │ ├── PULL_REQUEST_TEMPLATE.md │ ├── feature_request.md │ ├── security-issue.md │ └── bug_report.md ├── LICENSE ├── composer.json ├── SECURITY.md ├── CONTRIBUTING.md ├── README.md └── CODE_OF_CONDUCT.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vscode 3 | .DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `:package_name` will be documented in this file. 4 | -------------------------------------------------------------------------------- /src/config/livewire-doctor.php: -------------------------------------------------------------------------------- 1 | info('🩺 Running Livewire health check...'); 27 | 28 | $this->checkLivewireInstalled(); 29 | $this->checkAssetsPublished(); 30 | $this->checkBladeDirectives(); 31 | $this->checkComponentStructure(); 32 | 33 | $this->info('✅ Health check complete!'); 34 | } 35 | 36 | protected function checkLivewireInstalled(): void 37 | { 38 | if (!class_exists(\Livewire\Livewire::class)) { 39 | $this->error('❌ Livewire is not installed. Please run: composer require livewire/livewire'); 40 | } else { 41 | $version = \Composer\InstalledVersions::getVersion('livewire/livewire') ?? 'unknown'; 42 | $this->info("✅ Livewire is installed. Version: $version"); 43 | } 44 | } 45 | 46 | protected function checkAssetsPublished(): void 47 | { 48 | $publicPath = public_path('vendor/livewire/livewire.js'); 49 | 50 | if (!File::exists($publicPath)) { 51 | $this->warn('⚠️ Livewire assets not found. Run: php artisan livewire:publish --assets'); 52 | } else { 53 | $this->info('✅ Livewire assets are published.'); 54 | } 55 | } 56 | 57 | protected function checkBladeDirectives(): void 58 | { 59 | // Suggest checking master layout manually 60 | $this->info("🧠 Please ensure you have @livewireStyles in and @livewireScripts before ."); 61 | } 62 | 63 | protected function checkComponentStructure(): void 64 | { 65 | $componentPath = app_path('Livewire'); 66 | 67 | if (!File::isDirectory($componentPath)) { 68 | $this->warn('⚠️ No Livewire components found in: ' . $componentPath); 69 | } else { 70 | $components = collect(File::allFiles($componentPath)) 71 | ->filter(fn($file) => $file->getExtension() === 'php') 72 | ->map(fn($file) => $file->getFilename()); 73 | 74 | if ($components->isEmpty()) { 75 | $this->warn('⚠️ No Livewire component classes detected.'); 76 | } else { 77 | $this->info("✅ Found " . $components->count() . " Livewire components."); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/LivewireDoctor.php: -------------------------------------------------------------------------------- 1 | session = $session; 42 | $this->config = $config; 43 | } 44 | 45 | /** 46 | * Initializes and sets custom Livewire asset and update routes. 47 | * 48 | * This method: 49 | * - Checks if a custom `livewire.js` exists in the public directory and copies it from the vendor directory if missing. 50 | * - Overrides the default Livewire script route to point to the custom asset. 51 | * - Overrides the Livewire update route for dynamic Livewire component handling. 52 | * 53 | * @return void 54 | */ 55 | public static function initCustomAsset(): void 56 | { 57 | // Define paths 58 | $customAssetPath = public_path('vendor/devrabiul/livewire-doctor/dist/livewire.js'); 59 | $sourceAssetPath = base_path('vendor/livewire/livewire/dist/livewire.js'); 60 | 61 | // Copy asset if not already published 62 | if (!File::exists($customAssetPath) && File::exists($sourceAssetPath)) { 63 | File::ensureDirectoryExists(dirname($customAssetPath)); 64 | File::copy($sourceAssetPath, $customAssetPath); 65 | } 66 | 67 | // Define custom Livewire script route 68 | Livewire::setScriptRoute(function ($handle) { 69 | $scriptPath = realpath(dirname($_SERVER['SCRIPT_FILENAME'])); 70 | $basePath = realpath(base_path()); 71 | $publicPath = realpath(public_path()); 72 | 73 | $systemProcessingDirectory = $scriptPath === $publicPath ? 'public' 74 | : ($scriptPath === $basePath ? 'root' : 'unknown'); 75 | 76 | $basePathSegment = trim(request()->getBasePath(), '/'); 77 | 78 | $livewireJsPath = match ($systemProcessingDirectory) { 79 | 'public' => $basePathSegment 80 | ? "/{$basePathSegment}/vendor/devrabiul/livewire-doctor/dist/livewire.js" 81 | : "/vendor/devrabiul/livewire-doctor/dist/livewire.js", 82 | default => $basePathSegment 83 | ? "/{$basePathSegment}/public/vendor/devrabiul/livewire-doctor/dist/livewire.js" 84 | : "/public/vendor/devrabiul/livewire-doctor/dist/livewire.js", 85 | }; 86 | 87 | return Route::get($livewireJsPath, $handle); 88 | }); 89 | 90 | // Define custom Livewire update route 91 | Livewire::setUpdateRoute(function ($handle) { 92 | $scriptPath = realpath(dirname($_SERVER['SCRIPT_FILENAME'])); 93 | $basePath = realpath(base_path()); 94 | $publicPath = realpath(public_path()); 95 | 96 | $systemProcessingDirectory = $scriptPath === $publicPath ? 'public' 97 | : ($scriptPath === $basePath ? 'root' : 'unknown'); 98 | 99 | $basePathSegment = trim(request()->getBasePath(), '/'); 100 | 101 | $livewireUpdatePath = match ($systemProcessingDirectory) { 102 | 'public' => $basePathSegment 103 | ? "/{$basePathSegment}/livewire/update" 104 | : "/livewire/update", 105 | default => $basePathSegment 106 | ? "/{$basePathSegment}/public/livewire/update" 107 | : "/public/livewire/update", 108 | }; 109 | 110 | return Route::post($livewireUpdatePath, $handle); 111 | }); 112 | } 113 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🩺 LivewireDoctor – Fix Livewire Issues Instantly & Boost Productivity 2 | 3 | A powerful Laravel package that **automatically diagnoses, fixes**, and improves your **Livewire** development experience. Save time, eliminate headaches, and keep your components healthy — all with a single command. 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/devrabiul/livewire-doctor/v/stable)](https://packagist.org/packages/devrabiul/livewire-doctor) 6 | [![Total Downloads](https://poser.pugx.org/devrabiul/livewire-doctor/downloads)](https://packagist.org/packages/devrabiul/livewire-doctor) 7 | [![Monthly Downloads](https://poser.pugx.org/devrabiul/livewire-doctor/d/monthly)](https://packagist.org/packages/devrabiul/livewire-doctor) 8 | ![GitHub license](https://img.shields.io/github/license/devrabiul/livewire-doctor) 9 | [![Buy us a tree](https://img.shields.io/badge/Treeware-%F0%9F%8C%B3-lightgreen)](https://plant.treeware.earth/devrabiul/livewire-doctor) 10 | ![GitHub Repo stars](https://img.shields.io/github/stars/devrabiul/livewire-doctor?style=social) 11 | 12 | --- 13 | 14 | ## 🚀 Live Demo 15 | 16 | 👉 [Try the Live Demo](https://packages.rixetbd.com/devrabiul/livewire-doctor) 17 | 18 | ![Live Demo Thumbnail](https://packages.rixetbd.com/storage/app/public/package/devrabiul/livewire-doctor.webp) 19 | 20 | --- 21 | 22 | ## 💡 Overview 23 | 24 | **LivewireDoctor** is your personal development assistant for Laravel Livewire apps. It scans your project, detects common issues like missing assets, misconfigured directives, outdated components — and **fixes them automatically**. 25 | 26 | Whether you're debugging, onboarding a team, or simply maintaining Livewire-based applications — this package keeps your workflow smooth and efficient. 27 | 28 | --- 29 | 30 | ## ✨ Features at a Glance 31 | 32 | * ✅ Instantly diagnose common Livewire issues 33 | * 🛠️ Auto-fix configuration and structural problems 34 | * ⚙️ Artisan commands for one-click health checks 35 | * 🔍 Scans for performance tips and improvements 36 | * 🧰 Developer helpers for conditional debugging 37 | * ⚡ Optimized for **Livewire v3** 38 | * 🎯 Compatible with **Laravel 10 & 11** 39 | 40 | --- 41 | 42 | ## 📦 Installation 43 | 44 | Install via Composer: 45 | 46 | ```bash 47 | composer require devrabiul/livewire-doctor 48 | ``` 49 | 50 | Optionally publish the config (if required later): 51 | 52 | ```bash 53 | php artisan vendor:publish --provider="Devrabiul\LivewireDoctor\LivewireDoctorServiceProvider" 54 | ``` 55 | 56 | ## ⚙️ Initialize Custom Assets in `AppServiceProvider` 57 | 58 | You need to initialize custom Livewire assets `initCustomAsset()` method in your `AppServiceProvider`. 59 | 60 | #### ✅ Example: 61 | 62 | ```php 63 | ✅ “It just works! Saved me hours of debugging Livewire component issues.” 108 | > ✅ “Every Livewire project should start with `livewire:doctor`.” 109 | > ✅ “Incredible time-saver. One command, and my app is healthy again!” 110 | 111 | --- 112 | 113 | ## 🌍 Useful Links 114 | 115 | * 🔗 **GitHub:** [Livewire Doctor Repository](https://github.com/devrabiul/livewire-doctor) 116 | * 🔗 **Website:** [https://packages.rixetbd.com/devrabiul/livewire-doctor](https://packages.rixetbd.com/devrabiul/livewire-doctor) 117 | * 🔗 **Packagist:** [https://packagist.org/packages/devrabiul/livewire-doctor](https://packagist.org/packages/devrabiul/livewire-doctor) 118 | 119 | --- 120 | 121 | ## 🤝 Contributing 122 | 123 | We welcome contributions to LivewireDoctor! If you would like to contribute, please fork the repository and submit a pull request. For any issues or feature requests, please open an issue on GitHub. 124 | 125 | --- 126 | 127 | ## 📄 License 128 | 129 | Licensed under the [MIT License](LICENSE). 130 | 131 | --- 132 | 133 | ## 📬 Contact 134 | 135 | For support, bugs, or feature suggestions: 136 | 137 | * 📧 Email: [devrabiul@gmail.com](mailto:devrabiul@gmail.com) 138 | * 🌐 GitHub: [@devrabiul](https://github.com/devrabiul) 139 | 140 | --- 141 | **LivewireDoctor** is the smart way to keep your Laravel Livewire apps running at their best. Install it now — and let your code breathe easy! 142 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | devrabiul@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. -------------------------------------------------------------------------------- /src/LivewireDoctorServiceProvider.php: -------------------------------------------------------------------------------- 1 | updateProcessingDirectoryConfig(); 25 | $this->handleVersionedPublishing(); 26 | 27 | if ($this->app->runningInConsole()) { 28 | $this->registerPublishing(); 29 | } 30 | } 31 | 32 | /** 33 | * Register the package's publishable resources. 34 | * 35 | * This method registers: 36 | * - Configuration file publishing to the application's config directory. 37 | * - Asset publishing to the public vendor directory, replacing old assets if found. 38 | * 39 | * It is typically called when the application is running in console mode 40 | * to enable artisan vendor:publish commands. 41 | * 42 | * @return void 43 | */ 44 | private function registerPublishing(): void 45 | { 46 | $this->publishes([ 47 | __DIR__ . '/config/livewire-doctor.php' => config_path('livewire-doctor.php'), 48 | ]); 49 | } 50 | 51 | /** 52 | * Register any application services. 53 | * 54 | * This method: 55 | * - Loads the package config file if not already loaded. 56 | * - Registers a singleton instance of the LivewireDoctor class in the Laravel service container. 57 | * 58 | * This allows other parts of the application to resolve the 'LivewireDoctor' service. 59 | * 60 | * @return void 61 | */ 62 | public function register(): void 63 | { 64 | $configPath = config_path('livewire-doctor.php'); 65 | 66 | if (!file_exists($configPath)) { 67 | config(['livewire-doctor' => require __DIR__ . '/config/livewire-doctor.php']); 68 | } 69 | 70 | $this->app->singleton('LivewireDoctor', function ($app) { 71 | return new LivewireDoctor($app['session'], $app['config']); 72 | }); 73 | 74 | // ✅ Register command 75 | if ($this->app->runningInConsole()) { 76 | $this->commands([ 77 | LivewireDoctorCommand::class, 78 | ]); 79 | } 80 | } 81 | 82 | /** 83 | * Get the services provided by the provider. 84 | * 85 | * This method is used by Laravel's deferred providers mechanism 86 | * and lists the services that this provider registers. 87 | * 88 | * @return array Array of service container binding keys provided by this provider. 89 | */ 90 | public function provides(): array 91 | { 92 | return ['LivewireDoctor']; 93 | } 94 | 95 | /** 96 | * Determine and set the 'system_processing_directory' configuration value. 97 | * 98 | * This detects if the current PHP script is being executed from the public directory 99 | * or the project root directory, or neither, and sets a config value accordingly: 100 | * 101 | * - 'public' if script path equals public_path() 102 | * - 'root' if script path equals base_path() 103 | * - 'unknown' otherwise 104 | * 105 | * This config can be used internally to adapt asset loading or paths. 106 | * 107 | * @return void 108 | */ 109 | private function updateProcessingDirectoryConfig(): void 110 | { 111 | $scriptPath = realpath(dirname($_SERVER['SCRIPT_FILENAME'])); 112 | $basePath = realpath(base_path()); 113 | $publicPath = realpath(public_path()); 114 | 115 | if ($scriptPath === $publicPath) { 116 | $systemProcessingDirectory = 'public'; 117 | } elseif ($scriptPath === $basePath) { 118 | $systemProcessingDirectory = 'root'; 119 | } else { 120 | $systemProcessingDirectory = 'unknown'; 121 | } 122 | 123 | config(['livewire-doctor.system_processing_directory' => $systemProcessingDirectory]); 124 | } 125 | 126 | /** 127 | * Get the current installed version of the package from composer.lock. 128 | * 129 | * Reads and parses the composer.lock file located at the project root, 130 | * searches for the package 'devrabiul/livewire-doctor', 131 | * and returns the version string if found. 132 | * 133 | * Returns null if: 134 | * - composer.lock does not exist 135 | * - package is not found in composer.lock 136 | * 137 | * @return string|null Version string of the installed package, e.g. "1.0.1" or null if unavailable. 138 | */ 139 | private function getCurrentVersion(): ?string 140 | { 141 | $lockFile = base_path('composer.lock'); 142 | if (!file_exists($lockFile)) { 143 | return null; 144 | } 145 | 146 | $lockData = json_decode(file_get_contents($lockFile), true); 147 | $packages = $lockData['packages'] ?? []; 148 | 149 | foreach ($packages as $package) { 150 | if ($package['name'] === 'livewire/livewire') { 151 | return $package['version']; 152 | } 153 | } 154 | 155 | return null; 156 | } 157 | 158 | /** 159 | * Get the version recorded in the published version.php file. 160 | * 161 | * This file is expected to be located at: 162 | * `public/vendor/devrabiul/livewire-doctor/version.php` 163 | * 164 | * If the file exists and returns an array with a 'version' key, 165 | * that version string is returned. 166 | * 167 | * Returns null if the file does not exist or does not contain a version. 168 | * 169 | * @return string|null Previously published version string or null if none found. 170 | */ 171 | private function getPublishedVersion(): ?string 172 | { 173 | $versionFile = public_path('vendor/devrabiul/livewire-doctor/version.php'); 174 | 175 | if (!File::exists($versionFile)) { 176 | return null; 177 | } 178 | 179 | $versionData = include $versionFile; 180 | 181 | return $versionData['version'] ?? null; 182 | } 183 | 184 | /** 185 | * Publish the assets if the current package version differs from the published version. 186 | * 187 | * This method performs the following steps: 188 | * - Retrieves the current installed package version. 189 | * - Retrieves the previously published version from the public directory. 190 | * - If versions differ (or no published version exists), deletes the existing assets folder. 191 | * - Copies the new assets from the package's `assets` directory to the public vendor folder. 192 | * - Writes/updates the version.php file in the public folder with the current version. 193 | * 194 | * This ensures the public assets are always in sync with the installed package version. 195 | * 196 | * @return void 197 | */ 198 | private function handleVersionedPublishing(): void 199 | { 200 | $currentVersion = $this->getCurrentVersion(); 201 | $publishedVersion = $this->getPublishedVersion(); 202 | 203 | if ($currentVersion && $currentVersion !== $publishedVersion) { 204 | $assetsPath = public_path('vendor/devrabiul/livewire-doctor/dist'); 205 | $sourceAssets = base_path('vendor/livewire/livewire/dist'); 206 | 207 | // Ensure source assets exist before proceeding 208 | if (!File::exists($sourceAssets)) { 209 | logger()->warning("[LivewireDoctor] Source assets directory not found at: $sourceAssets"); 210 | return; 211 | } 212 | 213 | // Delete and re-create the target directory 214 | if (File::exists($assetsPath)) { 215 | File::deleteDirectory($assetsPath); 216 | } 217 | 218 | File::ensureDirectoryExists($assetsPath); 219 | 220 | // Copy the new assets 221 | File::copyDirectory($sourceAssets, $assetsPath); 222 | 223 | // Create version.php file with current version 224 | $versionPhpContent = " '{$currentVersion}',\n];\n"; 225 | File::put(public_path('vendor/devrabiul/livewire-doctor/version.php'), $versionPhpContent); 226 | 227 | logger()->info("[LivewireDoctor] Assets published for version: $currentVersion"); 228 | } 229 | } 230 | } 231 | 232 | --------------------------------------------------------------------------------