Skip to content

Commit 24a38f1

Browse files
committed
feat: enhance VendorFileMapper and CopyFromVendorCommand with file handling
1 parent 203d83f commit 24a38f1

2 files changed

Lines changed: 169 additions & 73 deletions

File tree

src/Console/Command/Theme/CopyFromVendorCommand.php

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,35 @@
1515
use Symfony\Component\Console\Input\InputInterface;
1616
use Symfony\Component\Console\Input\InputOption;
1717
use Symfony\Component\Console\Output\OutputInterface;
18+
use Magento\Framework\Filesystem\Driver\File;
1819

1920
use function Laravel\Prompts\confirm;
2021
use function Laravel\Prompts\search;
2122

2223
class CopyFromVendorCommand extends AbstractCommand
2324
{
25+
/**
26+
* @param ThemeList $themeList
27+
* @param VendorFileMapper $vendorFileMapper
28+
* @param DirectoryList $directoryList
29+
* @param ComponentRegistrarInterface $componentRegistrar
30+
* @param File $fileDriver
31+
*/
2432
public function __construct(
2533
private readonly ThemeList $themeList,
2634
private readonly VendorFileMapper $vendorFileMapper,
2735
private readonly DirectoryList $directoryList,
28-
private readonly ComponentRegistrarInterface $componentRegistrar
36+
private readonly ComponentRegistrarInterface $componentRegistrar,
37+
private readonly File $fileDriver
2938
) {
3039
parent::__construct();
3140
}
3241

42+
/**
43+
* Configure the command
44+
*
45+
* @return void
46+
*/
3347
protected function configure(): void
3448
{
3549
$this->setName('mageforge:theme:copy-from-vendor')
@@ -40,6 +54,13 @@ protected function configure(): void
4054
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Preview the copy operation without performing it');
4155
}
4256

57+
/**
58+
* Execute the command
59+
*
60+
* @param InputInterface $input
61+
* @param OutputInterface $output
62+
* @return int
63+
*/
4364
protected function executeCommand(InputInterface $input, OutputInterface $output): int
4465
{
4566
$sourceFileArg = $input->getArgument('file');
@@ -73,6 +94,13 @@ protected function executeCommand(InputInterface $input, OutputInterface $output
7394
return Cli::RETURN_SUCCESS;
7495
}
7596

97+
/**
98+
* Get absolute source path
99+
*
100+
* @param string $sourceFile
101+
* @return string
102+
* @throws \RuntimeException
103+
*/
76104
private function getAbsoluteSourcePath(string $sourceFile): string
77105
{
78106
$rootPath = $this->directoryList->getRoot();
@@ -82,13 +110,20 @@ private function getAbsoluteSourcePath(string $sourceFile): string
82110
$absoluteSourcePath = $rootPath . '/' . $sourceFile;
83111
}
84112

85-
if (!file_exists($absoluteSourcePath)) {
113+
if (!$this->fileDriver->isExists($absoluteSourcePath)) {
86114
throw new \RuntimeException("Source file not found: $absoluteSourcePath");
87115
}
88116

89117
return $absoluteSourcePath;
90118
}
91119

120+
/**
121+
* Get theme code
122+
*
123+
* @param InputInterface $input
124+
* @return string
125+
* @throws \RuntimeException
126+
*/
92127
private function getThemeCode(InputInterface $input): string
93128
{
94129
$themeCode = $input->getArgument('theme');
@@ -136,13 +171,22 @@ private function getThemePathAndArea(string $themeCode): array
136171
$themePath = $this->componentRegistrar->getPath(ComponentRegistrar::THEME, $regName);
137172

138173
if (!$themePath) {
139-
$this->io->warning("Theme path not found via ComponentRegistrar for $regName, falling back to getFullPath()");
174+
$this->io->warning(
175+
"Theme path not found via ComponentRegistrar for $regName, falling back to getFullPath()"
176+
);
140177
$themePath = $theme->getFullPath();
141178
}
142179

143180
return [$themePath, $themeArea];
144181
}
145182

183+
/**
184+
* Get absolute destination path
185+
*
186+
* @param string $destinationPath
187+
* @param string $rootPath
188+
* @return string
189+
*/
146190
private function getAbsoluteDestPath(string $destinationPath, string $rootPath): string
147191
{
148192
if (str_starts_with($destinationPath, '/')) {
@@ -151,6 +195,14 @@ private function getAbsoluteDestPath(string $destinationPath, string $rootPath):
151195
return $rootPath . '/' . $destinationPath;
152196
}
153197

198+
/**
199+
* Confirm copy operation
200+
*
201+
* @param string $sourceFile
202+
* @param string $absoluteDestPath
203+
* @param string $rootPath
204+
* @return bool
205+
*/
154206
private function confirmCopy(string $sourceFile, string $absoluteDestPath, string $rootPath): bool
155207
{
156208
$destinationDisplay = str_starts_with($absoluteDestPath, $rootPath . '/')
@@ -168,7 +220,7 @@ private function confirmCopy(string $sourceFile, string $absoluteDestPath, strin
168220
$this->setPromptEnvironment();
169221

170222
try {
171-
if (file_exists($absoluteDestPath)) {
223+
if ($this->fileDriver->isExists($absoluteDestPath)) {
172224
$this->io->warning("File already exists at destination!");
173225
$result = confirm(
174226
label: 'Overwrite existing file?',
@@ -193,17 +245,31 @@ private function confirmCopy(string $sourceFile, string $absoluteDestPath, strin
193245
}
194246
}
195247

248+
/**
249+
* Perform copy operation
250+
*
251+
* @param string $absoluteSourcePath
252+
* @param string $absoluteDestPath
253+
* @return void
254+
* @throws \RuntimeException
255+
*/
196256
private function performCopy(string $absoluteSourcePath, string $absoluteDestPath): void
197257
{
198-
$directory = dirname($absoluteDestPath);
199-
if (!is_dir($directory)) {
200-
if (!mkdir($directory, 0777, true) && !is_dir($directory)) {
201-
throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory));
202-
}
258+
$directory = $this->fileDriver->getParentDirectory($absoluteDestPath);
259+
if (!$this->fileDriver->isDirectory($directory)) {
260+
$this->fileDriver->createDirectory($directory);
203261
}
204-
copy($absoluteSourcePath, $absoluteDestPath);
262+
$this->fileDriver->copy($absoluteSourcePath, $absoluteDestPath);
205263
}
206264

265+
/**
266+
* Show dry run preview
267+
*
268+
* @param string $sourceFile
269+
* @param string $absoluteDestPath
270+
* @param string $rootPath
271+
* @return void
272+
*/
207273
private function showDryRunPreview(string $sourceFile, string $absoluteDestPath, string $rootPath): void
208274
{
209275
$destinationDisplay = str_starts_with($absoluteDestPath, $rootPath . '/')
@@ -218,7 +284,7 @@ private function showDryRunPreview(string $sourceFile, string $absoluteDestPath,
218284
]);
219285
$this->io->newLine();
220286

221-
if (file_exists($absoluteDestPath)) {
287+
if ($this->fileDriver->isExists($absoluteDestPath)) {
222288
$this->io->warning("File already exists at destination and would be overwritten!");
223289
} else {
224290
$this->io->info("File would be created at destination.");
@@ -227,6 +293,11 @@ private function showDryRunPreview(string $sourceFile, string $absoluteDestPath,
227293
$this->io->note("No files were modified (dry-run mode).");
228294
}
229295

296+
/**
297+
* Fix prompt environment
298+
*
299+
* @return void
300+
*/
230301
private function fixPromptEnvironment(): void
231302
{
232303
$this->setPromptEnvironment();

0 commit comments

Comments
 (0)