From a0b42b1434831788524755448f421db889158196 Mon Sep 17 00:00:00 2001 From: Jesse Leite Date: Sun, 10 Nov 2024 22:39:19 -0500 Subject: [PATCH] Fix and properly implement module folder scoping. --- src/StarterKits/InstallableModule.php | 32 ++++++++++++++++--- src/StarterKits/InstallableModules.php | 44 ++++++++++++++++++-------- src/StarterKits/Installer.php | 1 - tests/StarterKits/InstallTest.php | 38 ++++++++++++++-------- 4 files changed, 82 insertions(+), 33 deletions(-) diff --git a/src/StarterKits/InstallableModule.php b/src/StarterKits/InstallableModule.php index d30c8ffadb..79c8fa367e 100644 --- a/src/StarterKits/InstallableModule.php +++ b/src/StarterKits/InstallableModule.php @@ -13,6 +13,7 @@ final class InstallableModule extends Module { protected $installer; + protected $relativePath; /** * Set installer instance. @@ -26,6 +27,16 @@ public function installer(?Installer $installer): self return $this; } + /** + * Set relative module path. + */ + public function setRelativePath(string $path): self + { + $this->relativePath = $path; + + return $this; + } + /** * Validate starter kit module is installable. * @@ -125,8 +136,10 @@ protected function installableFiles(): Collection */ protected function expandExportDirectoriesToFiles(string $to, ?string $from = null): Collection { + $from = $this->relativePath($from ?? $to); + + $from = Path::tidy($this->starterKitPath($from)); $to = Path::tidy($this->starterKitPath($to)); - $from = Path::tidy($from ? $this->starterKitPath($from) : $to); $paths = collect([$from => $to]); @@ -184,9 +197,8 @@ protected function installableDependencies(string $configKey): array protected function ensureInstallableFilesExist(): self { $this - ->exportPaths() - ->merge($this->exportAsPaths()) - ->reject(fn ($path) => $this->files->exists($this->starterKitPath($path))) + ->installableFiles() + ->reject(fn ($to, $from) => $this->files->exists($from)) ->each(function ($path) { throw new StarterKitException("Starter kit path [{$path}] does not exist."); }); @@ -238,6 +250,18 @@ protected function starterKitPath(?string $path = null): string return collect([base_path("vendor/{$package}"), $path])->filter()->implode('/'); } + /** + * Get relative module path. + */ + protected function relativePath(string $path): string + { + if (! $this->relativePath) { + return $path; + } + + return Str::ensureRight($this->relativePath, '/').$path; + } + /** * Normalize packages array to require args, with version handling if `package => version` array structure is passed. */ diff --git a/src/StarterKits/InstallableModules.php b/src/StarterKits/InstallableModules.php index a05735998b..ef56cb5ae7 100644 --- a/src/StarterKits/InstallableModules.php +++ b/src/StarterKits/InstallableModules.php @@ -73,21 +73,22 @@ public function instantiate(): self /** * Recursively instantiate module and its nested modules. */ - protected function instantiateModuleRecursively(Collection|array|string $config, string $key): InstallableModule + protected function instantiateModuleRecursively(Collection|array|string $config, string $key, ?string $moduleScope = null): InstallableModule { - if ($config === '@import') { + if ($imported = $config === '@import') { $config = $this->importModuleConfig($key); - } - - if (Arr::get($config, 'import') === '@config') { + } elseif ($imported = Arr::get($config, 'import') === '@config') { $config = $this->importModuleConfig($key)->merge($config); } + $moduleScope = $imported ? $key : $moduleScope; + if ($options = Arr::get($config, 'options')) { $config['options'] = collect($options) ->map(fn ($optionConfig, $optionKey) => $this->instantiateModuleRecursively( $optionConfig, $this->normalizeModuleKey($key, $optionKey), + $moduleScope, )); } @@ -96,10 +97,17 @@ protected function instantiateModuleRecursively(Collection|array|string $config, ->map(fn ($childConfig, $childKey) => $this->instantiateModuleRecursively( $childConfig, $this->normalizeModuleKey($key, $childKey), + $moduleScope, )); } - return (new InstallableModule($config, $key))->installer($this->installer); + $module = (new InstallableModule($config, $key))->installer($this->installer); + + if ($moduleScope) { + $this->scopeInstallableFiles($module, $moduleScope); + } + + return $module; } /** @@ -109,7 +117,7 @@ protected function instantiateModuleRecursively(Collection|array|string $config, */ protected function importModuleConfig(string $key): Collection { - $moduleConfig = $this->relativeModulePath('module.yaml', $key); + $moduleConfig = $this->relativeModulePath($key, 'module.yaml'); $absolutePath = $this->starterKitPath($moduleConfig); @@ -117,11 +125,7 @@ protected function importModuleConfig(string $key): Collection throw new StarterKitException("Starter kit module config [$moduleConfig] does not exist."); } - $config = collect(YAML::parse($this->files->get($absolutePath))); - - // TODO: prefix from in export paths - - return $config; + return collect(YAML::parse($this->files->get($absolutePath))); } /** @@ -157,9 +161,21 @@ protected function starterKitPath(?string $path = null): string /** * Assemble relative imported module path. */ - protected function relativeModulePath(string $path, string $key): string + protected function relativeModulePath(string $key, ?string $path = null): string + { + $base = 'modules/'.str_replace('.', '/', $key); + + return $path + ? $base.Str::ensureLeft($path, '/') + : $base; + } + + /** + * Scope installable files to imported module. + */ + protected function scopeInstallableFiles(InstallableModule $module, string $scope): void { - return 'modules/'.str_replace('.', '/', $key).Str::ensureLeft($path, '/'); + $module->setRelativePath($this->relativeModulePath($scope)); } /** diff --git a/src/StarterKits/Installer.php b/src/StarterKits/Installer.php index 35c8503990..ff5a54211b 100644 --- a/src/StarterKits/Installer.php +++ b/src/StarterKits/Installer.php @@ -7,7 +7,6 @@ use Facades\Statamic\StarterKits\Hook; use Illuminate\Console\Command; use Illuminate\Filesystem\Filesystem; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Http; use Statamic\Console\NullConsole; use Statamic\Console\Please\Application as PleaseApplication; diff --git a/tests/StarterKits/InstallTest.php b/tests/StarterKits/InstallTest.php index 938a4f4a74..834da8322d 100644 --- a/tests/StarterKits/InstallTest.php +++ b/tests/StarterKits/InstallTest.php @@ -973,7 +973,7 @@ public function it_installs_imported_modules_confirmed_interactively_via_prompt( path: 'modules/seo/module.yaml', config: [ 'export_paths' => [ - 'resources/css/seo.css', + $this->moveKitRepoFile('modules/seo', 'resources/css/seo.css'), ], 'dependencies' => [ 'statamic/seo-pro' => '^0.2.0', @@ -987,13 +987,13 @@ public function it_installs_imported_modules_confirmed_interactively_via_prompt( 'options' => [ 'react' => [ 'export_paths' => [ - 'resources/js/react.js', + $this->moveKitRepoFile('modules/js', 'resources/js/react.js'), ], ], 'vue' => '@import', // import option as separate module! 'svelte' => [ 'export_paths' => [ - 'resources/js/svelte.js', + $this->moveKitRepoFile('modules/js', 'resources/js/svelte.js'), ], ], ], @@ -1004,7 +1004,7 @@ public function it_installs_imported_modules_confirmed_interactively_via_prompt( path: 'modules/js/vue/module.yaml', config: [ 'export_paths' => [ - 'resources/js/vue.js', + $this->moveKitRepoFile('modules/js/vue', 'resources/js/vue.js'), ], 'dependencies' => [ 'bobsled/vue-components' => '^1.5', @@ -1154,7 +1154,7 @@ public function it_can_merge_imported_module_config_with_starter_kit_config() config: [ 'label' => 'This should not get used, because prompt config in starter-kit.yaml takes precedence!', 'export_paths' => [ - 'resources/js/react.js', + $this->moveKitRepoFile('modules/js/react', 'resources/js/react.js'), ], ], ); @@ -1656,7 +1656,7 @@ public function it_installs_nested_imported_modules_confirmed_interactively_via_ path: 'modules/seo/module.yaml', config: [ 'export_paths' => [ - 'resources/css/seo.css', + $this->moveKitRepoFile('modules/seo', 'resources/css/seo.css'), ], 'dependencies' => [ 'statamic/seo-pro' => '^0.2.0', @@ -1666,19 +1666,19 @@ public function it_installs_nested_imported_modules_confirmed_interactively_via_ 'options' => [ 'react' => [ 'export_paths' => [ - 'resources/js/react.js', + $this->moveKitRepoFile('modules/seo', 'resources/js/react.js'), ], 'modules' => [ 'testing_tools' => [ 'export_paths' => [ - 'resources/js/react-testing-tools.js', + $this->moveKitRepoFile('modules/seo', 'resources/js/react-testing-tools.js'), ], ], ], ], 'vue' => [ 'export_paths' => [ - 'resources/js/vue.js', + $this->moveKitRepoFile('modules/seo', 'resources/js/vue.js'), ], 'dependencies_dev' => [ 'i-love-vue/test-helpers' => '^1.5', @@ -1689,7 +1689,7 @@ public function it_installs_nested_imported_modules_confirmed_interactively_via_ ], 'svelte' => [ 'export_paths' => [ - 'resources/js/svelte.js', + $this->moveKitRepoFile('modules/seo', 'resources/js/svelte.js'), ], ], ], @@ -1698,12 +1698,12 @@ public function it_installs_nested_imported_modules_confirmed_interactively_via_ 'options' => [ 'jquery' => [ 'export_paths' => [ - 'resources/js/jquery.js', + $this->moveKitRepoFile('modules/seo', 'resources/js/jquery.js'), ], ], 'mootools' => [ 'export_paths' => [ - 'resources/js/jquery.js', + $this->moveKitRepoFile('modules/seo', 'resources/js/mootools.js'), ], ], ], @@ -1716,7 +1716,7 @@ public function it_installs_nested_imported_modules_confirmed_interactively_via_ path: 'modules/seo/js/vue/testing_tools/module.yaml', config: [ 'export_paths' => [ - 'resources/js/vue-testing-tools.js', + $this->moveKitRepoFile('modules/seo/js/vue/testing_tools', 'resources/js/vue-testing-tools.js'), ], ], ); @@ -1725,7 +1725,7 @@ public function it_installs_nested_imported_modules_confirmed_interactively_via_ path: 'modules/jamaica/bobsled/module.yaml', config: [ 'export_paths' => [ - 'resources/css/bobsled.css', + $this->moveKitRepoFile('modules/jamaica/bobsled', 'resources/css/bobsled.css'), ], 'dependencies' => [ 'bobsled/speed-calculator' => '^1.0.0', @@ -1798,6 +1798,16 @@ private function setConfig($config, $path = 'starter-kit.yaml') $this->files->put($this->preparePath($this->kitRepoPath($path)), YAML::dump($config)); } + private function moveKitRepoFile($relativeModulePath, $relativeFilePath) + { + $this->files->move( + $this->kitRepoPath($relativeFilePath), + $this->preparePath($this->kitRepoPath(Str::ensureRight($relativeModulePath, '/').$relativeFilePath)), + ); + + return $relativeFilePath; + } + private function preparePath($path) { $folder = preg_replace('/(.*)\/[^\/]+\.[^\/]+/', '$1', $path);