Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.x] Add new package folder convention for starter kits #11119

Open
wants to merge 18 commits into
base: 5.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 36 additions & 22 deletions src/Console/Commands/StarterKitExport.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class StarterKitExport extends Command
*/
public function handle()
{
if (! File::exists(base_path('starter-kit.yaml'))) {
return $this->askToStubStarterKitConfig();
if ($this->isUsingLegacyExporterConventions()) {
$this->askToMigrateToPackageFolder();
}

if (! File::exists($path = $this->getAbsolutePath())) {
Expand All @@ -58,26 +58,6 @@ public function handle()
$this->components->info("Starter kit was successfully exported to [$path].");
}

/**
* Ask to stub out starter kit config.
*/
protected function askToStubStarterKitConfig(): void
{
$stubPath = __DIR__.'/stubs/starter-kits/starter-kit.yaml.stub';
$newPath = base_path($config = 'starter-kit.yaml');

if ($this->input->isInteractive()) {
if (! confirm("Config [{$config}] does not exist. Would you like to create it now?", true)) {
return;
}
}

File::copy($stubPath, $newPath);

$this->comment("A new config has been created at [{$config}].");
$this->comment('Please configure your `export_paths` and re-run to begin your export!');
}

/**
* Get absolute path.
*/
Expand Down Expand Up @@ -105,4 +85,38 @@ protected function askToCreateExportPath(string $path): void

$this->components->info("A new directory has been created at [{$path}].");
}

/**
* Determine if dev sandbox has starter-kit.yaml at root and/or customized composer.json at target path.
*/
protected function isUsingLegacyExporterConventions(): bool
{
return File::exists(base_path('starter-kit.yaml'));
}

/**
* Determine if dev sandbox has starter-kit.yaml at root and/or customized composer.json at target path.
*/
protected function askToMigrateToPackageFolder(): void
{
if ($this->input->isInteractive()) {
if (! confirm('Config should now live in the [package] folder. Would you like Statamic to move it for you?', true)) {
return;
}
}

if (! File::exists($dir = base_path('package'))) {
File::makeDirectory($dir, 0755, true);
}

if (File::exists($starterKitConfig = base_path('starter-kit.yaml'))) {
File::move($starterKitConfig, base_path('package/starter-kit.yaml'));
$this->components->info('Starter kit config moved to [package/starter-kit.yaml].');
}

if (File::exists($packageComposerJson = $this->getAbsolutePath().'/composer.json')) {
File::move($packageComposerJson, base_path('package/composer.json'));
$this->components->info('Composer package config moved to [package/composer.json].');
}
}
}
16 changes: 14 additions & 2 deletions src/StarterKits/Concerns/InteractsWithFilesystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ protected function installFile(string $fromPath, string $toPath, Command|NullCon
}

/**
* Export starter kit path.
* Export relative path to starter kit.
*/
protected function exportPath(string $starterKitPath, string $from, ?string $to = null): void
protected function exportRelativePath(string $starterKitPath, string $from, ?string $to = null): void
{
$to = $to
? "{$starterKitPath}/{$to}"
Expand All @@ -43,6 +43,18 @@ protected function exportPath(string $starterKitPath, string $from, ?string $to
: $files->copy($from, $to);
}

/**
* Copy directory contents into, file by file so that it does not stomp the whole target directory.
*/
protected function copyDirectoryContentsInto(string $from, string $to): void
{
$files = app(Filesystem::class);

collect($files->allFiles($from))
->mapWithKeys(fn ($file) => [$from.'/'.$file->getRelativePathname() => $to.'/'.$file->getRelativePathname()])
->each(fn ($to, $from) => $files->copy(Path::tidy($from), $this->preparePath($to)));
}

/**
* Prepare path directory.
*/
Expand Down
8 changes: 6 additions & 2 deletions src/StarterKits/ExportableModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ public function export(string $starterKitPath): void
{
$this
->exportPaths()
->each(fn ($path) => $this->exportPath(
->each(fn ($path) => $this->exportRelativePath(
from: $path,
starterKitPath: $starterKitPath,
));

$this
->exportAsPaths()
->each(fn ($to, $from) => $this->exportPath(
->each(fn ($to, $from) => $this->exportRelativePath(
from: $from,
to: $to,
starterKitPath: $starterKitPath,
Expand Down Expand Up @@ -112,6 +112,10 @@ protected function ensureNotExportingComposerJson(): self
->merge($this->exportAsPaths())
->merge($this->exportAsPaths()->keys());

if ($flattenedExportPaths->contains('starter-kit.yaml')) {
throw new StarterKitException('Cannot export [starter-kit.yaml] config.');
}

if ($flattenedExportPaths->contains('composer.json')) {
throw new StarterKitException('Cannot export [composer.json]. Please use `dependencies` array!');
}
Expand Down
84 changes: 20 additions & 64 deletions src/StarterKits/Exporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Statamic\StarterKits\Concerns\InteractsWithFilesystem;
use Statamic\StarterKits\Exceptions\StarterKitException;
use Statamic\Support\Arr;
use Statamic\Support\Str;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class Exporter
Expand Down Expand Up @@ -52,9 +51,8 @@ public function export(): void
->instantiateModules()
->clearExportPath()
->exportModules()
->exportConfig()
->exportHooks()
->exportComposerJson();
->exportPackage();
}

/**
Expand All @@ -74,8 +72,12 @@ protected function validateExportPath(): self
*/
protected function validateConfig(): self
{
if (! $this->files->exists(base_path('starter-kit.yaml'))) {
throw new StarterKitException('Export config [starter-kit.yaml] does not exist.');
if (! $this->files->exists(base_path('package/starter-kit.yaml'))) {
throw new StarterKitException('Starter kit config [package/starter-kit.yaml] does not exist.');
}

if (! $this->files->exists(base_path('package/composer.json'))) {
throw new StarterKitException('Package config [package/composer.json] does not exist.');
}

return $this;
Expand Down Expand Up @@ -162,7 +164,9 @@ protected function clearExportPath()
*/
protected function exportModules(): self
{
$this->modules->each(fn ($module) => $module->export($this->exportPath));
$exportPath = $this->exportPath.'/export';

$this->modules->each(fn ($module) => $module->export($exportPath));

return $this;
}
Expand All @@ -172,7 +176,7 @@ protected function exportModules(): self
*/
protected function config(?string $key = null): mixed
{
$config = collect(YAML::parse($this->files->get(base_path('starter-kit.yaml'))));
$config = collect(YAML::parse($this->files->get(base_path('package/starter-kit.yaml'))));

if ($key) {
return $config->get($key);
Expand All @@ -181,20 +185,6 @@ protected function config(?string $key = null): mixed
return $config;
}

/**
* Export starter kit config.
*/
protected function exportConfig(): self
{
$config = $this
->versionModuleDependencies()
->syncConfigWithModules();

$this->files->put("{$this->exportPath}/starter-kit.yaml", YAML::dump($config->all()));

return $this;
}

/**
* Version module dependencies from composer.json.
*/
Expand Down Expand Up @@ -262,7 +252,7 @@ protected function exportHooks(): self

collect($hooks)
->filter(fn ($hook) => $this->files->exists(base_path($hook)))
->each(fn ($hook) => $this->exportPath(
->each(fn ($hook) => $this->exportRelativePath(
from: $hook,
starterKitPath: $this->exportPath,
));
Expand All @@ -271,52 +261,18 @@ protected function exportHooks(): self
}

/**
* Export composer.json.
* Export package config & other misc vendor files.
*/
protected function exportComposerJson(): self
protected function exportPackage(): self
{
$composerJson = $this->prepareComposerJsonFromStub()->all();

$this->files->put(
"{$this->exportPath}/composer.json",
json_encode($composerJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."\n"
);

return $this;
}
$this->copyDirectoryContentsInto(base_path('package'), $this->exportPath);

/**
* Prepare composer.json from stub.
*/
protected function prepareComposerJsonFromStub(): Collection
{
$stub = $this->getComposerJsonStub();

$directory = preg_replace('/.*\/([^\/]*)/', '$1', $this->exportPath);
$vendorName = $this->vendorName ?? 'my-vendor-name';
$repoName = Str::slug($directory);
$package = "{$vendorName}/{$repoName}";
$title = Str::slugToTitle($repoName);

$stub = str_replace('dummy/package', $package, $stub);
$stub = str_replace('DummyTitle', $title, $stub);

return collect(json_decode($stub, true));
}

/**
* Get composer.json stub.
*/
protected function getComposerJsonStub(): string
{
$stubPath = __DIR__.'/../Console/Commands/stubs/starter-kits/composer.json.stub';

$existingComposerJsonPath = "{$this->exportPath}/composer.json";
$config = $this
->versionModuleDependencies()
->syncConfigWithModules();

if ($this->files->exists($existingComposerJsonPath)) {
return $this->files->get($existingComposerJsonPath);
}
$this->files->put("{$this->exportPath}/starter-kit.yaml", YAML::dump($config->all()));

return $this->files->get($stubPath);
return $this;
}
}
33 changes: 24 additions & 9 deletions src/StarterKits/InstallableModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ protected function installableFiles(): Collection
*/
protected function expandExportDirectoriesToFiles(string $to, ?string $from = null): Collection
{
$to = Path::tidy($this->starterKitPath($to));
$from = Path::tidy($from ? $this->starterKitPath($from) : $to);
$to = Path::tidy($this->installableFilesPath($to));
$from = Path::tidy($from ? $this->installableFilesPath($from) : $to);

$paths = collect([$from => $to]);

Expand All @@ -139,13 +139,24 @@ protected function expandExportDirectoriesToFiles(string $to, ?string $from = nu
]);
}

$package = $this->installer->package();

return $paths->mapWithKeys(fn ($to, $from) => [
Path::tidy($from) => Path::tidy(str_replace("/vendor/{$package}", '', $to)),
Path::tidy($from) => Path::tidy($this->convertInstallableToDestinationPath($to)),
]);
}

/**
* Convert installable vendor file path to destination path.
*/
protected function convertInstallableToDestinationPath(string $path): string
{
$package = $this->installer->package();

$path = str_replace("/vendor/{$package}/export", '', $path);
$path = str_replace("/vendor/{$package}", '', $path);

return $path;
}

/**
* Install dependency permanently into app.
*/
Expand Down Expand Up @@ -186,7 +197,7 @@ protected function ensureInstallableFilesExist(): self
$this
->exportPaths()
->merge($this->exportAsPaths())
->reject(fn ($path) => $this->files->exists($this->starterKitPath($path)))
->reject(fn ($path) => $this->files->exists($this->installableFilesPath($path)))
->each(function ($path) {
throw new StarterKitException("Starter kit path [{$path}] does not exist.");
});
Expand Down Expand Up @@ -229,13 +240,17 @@ protected function ensureCanRequireDependencies(array $packages, bool $dev = fal
}

/**
* Get starter kit vendor path.
* Get starter kit installable files path.
*/
protected function starterKitPath(?string $path = null): string
protected function installableFilesPath(?string $path = null): string
{
$package = $this->installer->package();

return collect([base_path("vendor/{$package}"), $path])->filter()->implode('/');
$scope = $this->files->exists(base_path("vendor/{$package}/export"))
? 'export'
: null;

return collect([base_path("vendor/{$package}"), $scope, $path])->filter()->implode('/');
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/StarterKits/Installer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Statamic\Console\Please\Application as PleaseApplication;
use Statamic\Console\Processes\Exceptions\ProcessException;
use Statamic\Facades\Blink;
use Statamic\Facades\Path;
use Statamic\Facades\YAML;
use Statamic\StarterKits\Concerns\InteractsWithFilesystem;
use Statamic\StarterKits\Exceptions\StarterKitException;
Expand Down Expand Up @@ -664,7 +665,7 @@ protected function tidyComposerErrorOutput(string $output): string
*/
protected function starterKitPath(?string $path = null): string
{
return collect([base_path("vendor/{$this->package}"), $path])->filter()->implode('/');
return Path::tidy(collect([base_path("vendor/{$this->package}"), $path])->filter()->implode('/'));
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/StarterKits/Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ protected function exportPaths(): Collection

/**
* Get `export_as` paths (to be renamed on install) as collection from config.
*
* This is only here for backwards compatibility. Use new `export` folder convention instead.
*
* @deprecated
*/
protected function exportAsPaths(): Collection
{
Expand Down
Loading