diff --git a/src/Commands/RemoveComponentCommand.php b/src/Commands/RemoveComponentCommand.php index a9de8f7..5fd9619 100644 --- a/src/Commands/RemoveComponentCommand.php +++ b/src/Commands/RemoveComponentCommand.php @@ -2,11 +2,8 @@ namespace Sheaf\Cli\Commands; -use Sheaf\Cli\Services\ComponentInstaller; -use Sheaf\Cli\Support\InstallationConfig; use Illuminate\Console\Command; use Illuminate\Support\Arr; -use Illuminate\Support\Str; use Sheaf\Cli\Services\ComponentRemover; use function Laravel\Prompts\text; @@ -14,9 +11,6 @@ class RemoveComponentCommand extends Command { - public function __construct(protected ComponentRemover $componentRemover) { - parent::__construct(); - } /** * The name and signature of the console command. * @@ -39,14 +33,20 @@ public function handle() { $componentNames = $this->getComponentName(); + $success = Command::SUCCESS; + + $componentRemover = new ComponentRemover($this); foreach ($componentNames as $name) { $this->banner("Removing all $name files"); - $this->componentRemover->remove($name); + $success = $componentRemover->remove($name); + } + + if($success === Command::SUCCESS) { + $this->info("+ updated sheaf-lock.json and sheaf.json files"); } - $this->info("+ updated sheaf-lock.json and sheaf.json files"); return Command::SUCCESS; } diff --git a/src/Services/ComponentInstaller.php b/src/Services/ComponentInstaller.php index 6e8e07b..dde7200 100644 --- a/src/Services/ComponentInstaller.php +++ b/src/Services/ComponentInstaller.php @@ -126,6 +126,6 @@ public function handleOverwriteChoice() public function confirmDestructiveAction() { - return confirm("All the component files will be overwritten, you might lose your modifications. are you sure you want to processed?"); + return confirm("All the component files will be overwritten, you might lose your modifications. are you sure you want to proceed?"); } } diff --git a/src/Services/ComponentRemover.php b/src/Services/ComponentRemover.php index 21da607..13aa063 100644 --- a/src/Services/ComponentRemover.php +++ b/src/Services/ComponentRemover.php @@ -3,36 +3,48 @@ namespace Sheaf\Cli\Services; +use Illuminate\Console\Command; use Illuminate\Support\Facades\File; -use Symfony\Component\Console\Output\ConsoleOutput; - class ComponentRemover { protected $output; protected $componentName; - public function __construct() + public function __construct(protected $command) { - $this->output = new ConsoleOutput(); } public function remove($name) { $this->componentName = $name; + $isExists = $this->checkComponentExistence(); + + if(!$isExists) { + $this->message("Component is not installed in this project."); + return Command::FAILURE; + } + $this->deleteComponentFiles(); $this->cleaningSheafLock($name); } + protected function checkComponentExistence() + { + + return File::exists(resource_path("views/components/ui/{$this->componentName}")) || + File::exists(resource_path("views/components/ui/{$this->componentName}.blade.php")); + } + protected function deleteComponentFiles() { $componentDirectory = resource_path("views/components/ui/{$this->componentName}"); if (File::isDirectory($componentDirectory)) { File::deleteDirectory($componentDirectory); - $this->message("+ Deleted directory: $componentDirectory"); + $this->message("+ Deleted directory: resources/views/components/ui/{$this->componentName}"); } $componentFile = resource_path("views/components/ui/{$this->componentName}.blade.php"); @@ -74,11 +86,15 @@ protected function cleaningFiles(&$sheafLock) protected function cleaningDependencies(&$sheafLock) { + if(!isset($sheafLock['internalDependencies'])) { + return; + } + foreach ($sheafLock['internalDependencies'] as $dep => $components) { $remainingComponents = $this->removeComponentFromList($components); if (empty($remainingComponents)) { - $remover = new self(); + $remover = new self($this->command); $remover->remove($dep); unset($sheafLock['internalDependencies'][$dep]); @@ -91,6 +107,10 @@ protected function cleaningDependencies(&$sheafLock) protected function cleaningHelpers(&$sheafLock) { + if(!isset($sheafLock['helpers'])) { + return; + } + foreach ($sheafLock['helpers'] as $helper => $components) { $remainingComponents = $this->removeComponentFromList($components); @@ -136,6 +156,6 @@ protected function deleteHelperFile($helper) protected function message(string $message) { - $this->output->writeln("$message"); + $this->command->info("$message"); } } diff --git a/tests/Feature/Commands/InstallCommandTest.php b/tests/Feature/Commands/InstallCommandTest.php index 7c71de1..415aaaf 100644 --- a/tests/Feature/Commands/InstallCommandTest.php +++ b/tests/Feature/Commands/InstallCommandTest.php @@ -1,30 +1,14 @@ artisan("sheaf:install separator") ->assertExitCode(0) ->run(); $this->view('components.ui.separator.index'); + $this->artisan("sheaf:remove separator"); }); it("installs a component along with its dependencies when confirmed", function () { @@ -35,24 +19,27 @@ ->run(); $this->view("components.ui.radio.group", ['slot' => 'default']); + + $this->artisan("sheaf:remove radio"); }); it("overwrites existing component files when forced", function () { $this->artisan("sheaf:install separator") - ->assertExitCode(0) - ->run(); + ->assertExitCode(0) + ->run(); $this->artisan("sheaf:install separator") - ->expectsQuestion("Component 'Separator' already exists. What would you like to do?", "overwrite") - ->expectsQuestion("All the component files will be overwritten, you might lose your modifications. are you sure you want to processed?", "yes") - ->expectsOutputToContain("All component files will be overwritten.") - ->assertExitCode(0) - ->run(); + ->expectsQuestion("Component 'Separator' already exists. What would you like to do?", "overwrite") + ->expectsQuestion("All the component files will be overwritten, you might lose your modifications. are you sure you want to proceed?", "yes") + ->expectsOutputToContain("All component files will be overwritten.") + ->assertExitCode(0) + ->run(); $this->view("components.ui.separator.index"); + $this->artisan("sheaf:remove separator"); }); it("installs only dependencies when the component already exists and that option is chosen", function () { @@ -60,26 +47,27 @@ $command = 'sheaf:install separator'; $this->artisan($command) - ->assertExitCode(0) - ->run(); + ->assertExitCode(0) + ->run(); $this->artisan($command) - ->expectsQuestion("Component 'Separator' already exists. What would you like to do?", "dependencies") - ->expectsOutputToContain("Skipping component files, checking dependencies...") - ->assertExitCode(0) - ->run(); + ->expectsQuestion("Component 'Separator' already exists. What would you like to do?", "dependencies") + ->expectsOutputToContain("Skipping component files, checking dependencies...") + ->assertExitCode(0) + ->run(); $this->view("components.ui.separator.index"); + $this->artisan("sheaf:remove separator"); }); it("simulates component installation with the dry-run option", function () { $this->artisan("sheaf:install alerts --dry-run") - ->expectsOutputToContain("Preview: Installing Alerts (Dry Run)") - ->expectsOutputToContain("Will create") - ->assertExitCode(0) - ->run(); + ->expectsOutputToContain("Preview: Installing Alerts (Dry Run)") + ->expectsOutputToContain("Will create") + ->assertExitCode(0) + ->run(); expect(view()->exists("components.ui.alerts.index"))->toBeFalse(); -}); \ No newline at end of file +}); diff --git a/tests/Feature/Commands/RemoveCommandTest.php b/tests/Feature/Commands/RemoveCommandTest.php new file mode 100644 index 0000000..026e038 --- /dev/null +++ b/tests/Feature/Commands/RemoveCommandTest.php @@ -0,0 +1,88 @@ +artisan("sheaf:install $component") + ->assertExitCode(0) + ->run(); + + + expect(File::isDirectory($componentDirectory))->toBeTrue(); + + $this->view("components.ui.$component.index"); + + $this->artisan("sheaf:remove $component") + ->assertExitCode(0) + ->expectsOutputToContain("Deleted directory: resources/views/components/ui/$component") + ->run(); + + expect(File::isDirectory($componentDirectory))->toBeFalse(); + + $sheafLock = File::get(base_path("sheaf-lock.json")); + + expect($sheafLock)->not->toContain("$component"); +}); + +it("quits successfully when attempting to remove a non-installed component", function () { + + $component = 'modal'; + $componentDirectory = resource_path("views/components/ui/$component"); + + + $this->artisan("sheaf:remove $component") + ->assertExitCode(0) + ->expectsOutputToContain("Component is not installed in this project.") + ->run(); + + expect(File::isDirectory($componentDirectory))->toBeFalse(); + + $sheafLock = File::get(base_path("sheaf-lock.json")); + + expect($sheafLock)->not->toContain("$component"); +}); + + +it("removes an installed component with dependencies", function () { + + $component = 'select'; + $dependency = 'icon'; + $helper = 'popup'; + $baseDirectory = resource_path("views/components/ui/"); + + //* using force option to ensure the command runs + $this->artisan("sheaf:install $component --force --internal-deps") + ->assertExitCode(0) + ->run(); + + expect(File::isDirectory("$baseDirectory/$component"))->toBeTrue(); + expect(File::isDirectory("$baseDirectory/$dependency"))->toBeTrue(); + expect(File::exists("$baseDirectory/$helper.blade.php"))->toBeTrue(); + + expect(view()->exists("components.ui.$component.index"))->toBeTrue(); + expect(view()->exists("components.ui.$dependency.index"))->toBeTrue(); + expect(view()->exists("components.ui.$helper"))->toBeTrue(); + + + $this->artisan("sheaf:remove $component") + ->assertExitCode(0) + ->expectsOutputToContain("Deleted directory: resources/views/components/ui/$component") + ->expectsOutputToContain("Deleted directory: resources/views/components/ui/$dependency") + ->expectsOutputToContain("Removed internal dependency: $dependency (no longer used.)") + ->expectsOutputToContain("Removed helper: $helper (no longer used.)") + ->run(); + + expect(File::isDirectory("$baseDirectory/$component"))->toBeFalse(); + expect(File::isDirectory("$baseDirectory/$dependency"))->toBeFalse(); + + $sheafLock = File::get(base_path("sheaf-lock.json")); + + expect($sheafLock)->not->toContain("$component"); + expect($sheafLock)->not->toContain("$dependency"); +});