Skip to content
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
2 changes: 1 addition & 1 deletion src/CLI/CLI.php
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ public function isDefined(string $arg): bool
/**
* Creates pretty json
*/
public function json(array $data = []): string
public function json(array|object $data = []): string
{
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
Expand Down
137 changes: 137 additions & 0 deletions src/CLI/Commands/Migrate/To/PublicFolder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Kirby\CLI\Command;
use Kirby\Filesystem\Dir;
use Kirby\Filesystem\F;
use stdClass;

class PublicFolder extends Command
{
Expand Down Expand Up @@ -37,6 +38,7 @@ public static function command(CLI $cli): void

static::makeIndexPHP($cli, $publicDir);
static::removeOldIndexPHP($cli);
static::updateComposerConfig($cli);

$cli->br();
$cli->success('Migrated to a public folder setup');
Expand All @@ -49,6 +51,15 @@ protected static function confirmMigration(CLI $cli): void
$cli->br();
}

/**
* The document root that the built-in server has to
* serve after the migration. Null if there is none.
*/
protected static function documentRoot(CLI $cli): string|null
{
return basename(static::publicDir($cli->dir()));
}

protected static function makeIndexPHP(CLI $cli, string $publicDir)
{
$template = $cli->root('commands.core') . '/migrate/to/_templates/index.public.simple.php';
Expand Down Expand Up @@ -171,4 +182,130 @@ protected static function removeOldIndexPHP(CLI $cli): void
}
}

/**
* Points the start script in the composer.json
* at the document root of the new setup
*/
protected static function updateComposerConfig(CLI $cli): void
{
$file = $cli->dir() . '/composer.json';

if (is_file($file) === false) {
return;
}

$contents = F::read($file);

if (is_string($contents) === false) {
$cli->out('🚨 The composer.json could not be read');
return;
}

// decoding into objects keeps empty objects, like an
// `"extra": {}`, from turning into arrays when writing back
$composer = json_decode($contents);

if ($composer instanceof stdClass === false) {
$cli->out('🚨 The composer.json could not be parsed');
return;
}

$start = $composer->scripts->start ?? null;

if ($start === null) {
return;
}

// the start script can be a single command or a list of commands
$commands = is_string($start) === true ? [$start] : $start;

if (is_array($commands) === false) {
return;
}

$root = static::documentRoot($cli);
$server = false;
$updated = false;

foreach ($commands as $key => $command) {
if (is_string($command) === false) {
continue;
}

$new = static::updateStartCommand($command, $root);

// the command does not start a server
if ($new === null) {
continue;
}

$server = true;

if ($new !== $command) {
$commands[$key] = $new;
$updated = true;
}
}

if ($server === false) {
$cli->out('🚨 The start script in the composer.json could not be updated. Please set the document root manually.');
return;
}

// the start script already serves the right document root
if ($updated === false) {
return;
}

$composer->scripts->start = is_string($start) === true ? $commands[0] : $commands;

$json = $cli->json($composer);

// keep the trailing newline of the original file
if (str_ends_with($contents, "\n") === true) {
$json .= "\n";
}

if (F::write($file, $json) === true) {
$cli->out('✅ The composer.json has been updated');
} else {
$cli->out('🚨 The composer.json could not be updated');
}
}

/**
* Sets the document root of a command that starts the
* built-in server, or removes it if there is no document
* root. Returns null if the command starts no server.
*/
protected static function updateStartCommand(
string $command,
string|null $root
): string|null {
$parts = preg_split('!\s+!', trim($command)) ?: [];

// only the built-in server takes a document root
if (in_array('-S', $parts, true) === false) {
return null;
}

// remove the current document root
$target = array_search('-t', $parts, true);

if ($target !== false) {
array_splice($parts, $target, 2);
}

// add the new document root right after the host and port
if ($root !== null) {
$server = array_search('-S', $parts, true);

if ($server !== false) {
array_splice($parts, $server + 2, 0, ['-t', $root]);
}
}

return implode(' ', $parts);
}

}
10 changes: 10 additions & 0 deletions src/CLI/Commands/Migrate/To/RootFolder.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,21 @@ public static function command(CLI $cli): void

static::makeIndexPHP($cli, $dir);
static::removePublicDir($cli, $publicDir);
static::updateComposerConfig($cli);

$cli->br();
$cli->success('Migrated to a root folder setup');
}

/**
* A root folder setup is served from the project
* root, so the start script needs no document root
*/
protected static function documentRoot(CLI $cli): string|null
{
return null;
}

protected static function makeIndexPHP(CLI $cli, string $dir)
{
$template = $cli->root('commands.core') . '/migrate/to/_templates/index.root.simple.php';
Expand Down
30 changes: 30 additions & 0 deletions tests/CLI/Commands/Migrate/To/PublicFolderProxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types = 1);

namespace Kirby\CLI\Commands\Migrate\To;

use Kirby\CLI\CLI;

/**
* Opens up the protected migration helpers for testing
*/
class PublicFolderProxy extends PublicFolder
{
public static function documentRoot(CLI $cli): string|null
{
return parent::documentRoot($cli);
}

public static function updateComposerConfig(CLI $cli): void
{
parent::updateComposerConfig($cli);
}

public static function updateStartCommand(
string $command,
string|null $root
): string|null {
return parent::updateStartCommand($command, $root);
}
}
Loading
Loading