Skip to content
Merged
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
1 change: 1 addition & 0 deletions HYDEPHP_V3_PLANNING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Having this document in code lets us know the devlopment state at any given poin
- Fixed documentation search index files leaking into the generated sitemap: `search.json` (and any other page compiled to a non-HTML output file) no longer appears in `sitemap.xml`. The sitemap generator now asks each page through `HydePage::showInSitemap()` instead of only filtering out redirect pages.
- The `Redirect` page class constructor now accepts an optional `$matter` parameter, used by the framework to hide the generated documentation root redirect from navigation menus. Existing usages are unaffected.
- The realtime compiler now resolves registered page routes before proxying static assets, replacing the hardcoded `search.json` exemption, so `hyde serve` serves any registered route regardless of its output extension. Registered pages now always win over a static file at the same path; the previous behavior of serving such a shadowing file only affected the dev server and no real setups are expected to be affected.
- `hyde serve` now serves media files from a custom media directory. In v2 the dev server only knew the default `_media` to `media` convention, so a project using the `media_directory` config option got 404s for all its media while previewing the site, even though the built site was correct. The serve command now passes the resolved directories to the server process, which keeps media requests on the fast path that skips booting the application.

- Removed `Hyde\Markdown\Processing\CodeblockFilepathProcessor`, along with the `<!-- HYDE[Filepath] -->` marker comments it passed between its own pre- and post-processing steps. Both were internal implementation details: the processor list is hardcoded in an internal trait, so there was no supported way to register the class, and the markers only ever existed part-way through a single conversion. Neither is documented in the changelog or upgrade guide for that reason. Labels are now resolved on the syntax tree by `PrepareCodeBlocks`.
- Changed the generated HTML for fenced code blocks, which now comes from the Blade view. Site output is not part of the backward compatibility promise, so this is noted for awareness rather than as a breaking change. The `hyde-code-block` and `hyde-code-block-label` classes are stable hooks for projects styling code blocks from their own CSS.
Expand Down
18 changes: 15 additions & 3 deletions packages/framework/tests/Feature/Commands/ServeCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,11 @@ public function testHydeServeCommandPassesThroughProcessOutput()

Process::shouldReceive('env')
->once()
->with(['HYDE_SERVER_REQUEST_OUTPUT' => false])
->with([
'HYDE_SERVER_REQUEST_OUTPUT' => false,
'HYDE_SERVER_MEDIA_DIRECTORY' => '_media',
'HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY' => 'media',
])
->andReturnSelf();

Process::shouldReceive('start')
Expand Down Expand Up @@ -205,7 +209,11 @@ public function testHydeServeCommandWithViteOption()

Process::shouldReceive('env')
->once()
->with(['HYDE_SERVER_REQUEST_OUTPUT' => false])
->with([
'HYDE_SERVER_REQUEST_OUTPUT' => false,
'HYDE_SERVER_MEDIA_DIRECTORY' => '_media',
'HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY' => 'media',
])
->andReturnSelf();

Process::shouldReceive('start')
Expand Down Expand Up @@ -254,7 +262,11 @@ public function testHydeServeCommandWithViteOptionButViteNotRunning()

Process::shouldReceive('env')
->once()
->with(['HYDE_SERVER_REQUEST_OUTPUT' => false])
->with([
'HYDE_SERVER_REQUEST_OUTPUT' => false,
'HYDE_SERVER_MEDIA_DIRECTORY' => '_media',
'HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY' => 'media',
])
->andReturnSelf();

Process::shouldReceive('start')
Expand Down
21 changes: 21 additions & 0 deletions packages/framework/tests/Unit/ServeCommandOptionsUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Hyde\Framework\Testing\Unit;

use Mockery;
use Hyde\Hyde;
use Hyde\Testing\UnitTestCase;
use Hyde\Foundation\HydeKernel;
use Illuminate\Process\Factory;
Expand All @@ -20,6 +21,8 @@
#[\PHPUnit\Framework\Attributes\CoversClass(\Hyde\RealtimeCompiler\Console\Commands\ServeCommand::class)]
class ServeCommandOptionsUnitTest extends UnitTestCase
{
protected static bool $needsKernel = true;

protected function setUp(): void
{
self::mockConfig([
Expand Down Expand Up @@ -88,16 +91,34 @@ public function testGetEnvironmentVariables()
{
$this->assertSame([
'HYDE_SERVER_REQUEST_OUTPUT' => true,
'HYDE_SERVER_MEDIA_DIRECTORY' => '_media',
'HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY' => 'media',
], $this->getMock()->getEnvironmentVariables());
}

public function testGetEnvironmentVariablesWithNoAnsiOption()
{
$this->assertSame([
'HYDE_SERVER_REQUEST_OUTPUT' => false,
'HYDE_SERVER_MEDIA_DIRECTORY' => '_media',
'HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY' => 'media',
], $this->getMock(['no-ansi' => true])->getEnvironmentVariables());
}

public function testGetEnvironmentVariablesWithCustomMediaDirectory()
{
Hyde::setMediaDirectory('_custom-media');

try {
$environment = $this->getMock()->getEnvironmentVariables();

$this->assertSame('_custom-media', $environment['HYDE_SERVER_MEDIA_DIRECTORY']);
$this->assertSame('custom-media', $environment['HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY']);
} finally {
Hyde::setMediaDirectory('_media');
}
}

public function testSavePreviewOptionPropagatesToEnvironmentVariables()
{
$command = $this->getMock(['save-preview' => 'false']);
Expand Down
25 changes: 22 additions & 3 deletions packages/realtime-compiler/src/Actions/AssetFileLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ public static function find(string $path): ?string
return $static;
}

// TODO: Custom media directories are unsupported because media is proxied before the application boots.
if (str_starts_with($path, 'media/')) {
$media = BASE_PATH.'/_media/'.substr($path, strlen('media/'));
if (static::isMediaPath($path)) {
$media = BASE_PATH.'/'.static::mediaDirectory().'/'.substr($path, strlen(static::mediaOutputDirectory()) + 1);

if (is_file($media)) {
return $media;
Expand All @@ -30,4 +29,24 @@ public static function find(string $path): ?string

return null;
}

public static function isMediaPath(string $path): bool
{
return str_starts_with(trim($path, '/'), static::mediaOutputDirectory().'/');
}

/**
* The serve command resolves the configured media directories and passes them to the server
* process, as media is proxied before the application boots. The defaults apply when the
* server is started directly, for example through the Herd integration.
*/
protected static function mediaDirectory(): string
{
return getenv('HYDE_SERVER_MEDIA_DIRECTORY') ?: '_media';
}

protected static function mediaOutputDirectory(): string
{
return getenv('HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY') ?: 'media';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ protected function getEnvironmentVariables(): array
'HYDE_SERVER_DASHBOARD' => $this->parseEnvironmentOption('dashboard'),
'HYDE_PRETTY_URLS' => $this->parseEnvironmentOption('pretty-urls'),
'HYDE_PLAY_CDN' => $this->parseEnvironmentOption('play-cdn'),
'HYDE_SERVER_MEDIA_DIRECTORY' => Hyde::getMediaDirectory(),
'HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY' => Hyde::getMediaOutputDirectory(),
]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public function getRoutePreviewLink(Route $route): string

public function getMediaPreviewLink(MediaFile $mediaFile): string
{
return $this->rootRelativeLink('media/'.$mediaFile->getIdentifier());
return $this->rootRelativeLink(Hyde::getMediaOutputDirectory().'/'.$mediaFile->getIdentifier());
}

/** @return array{label: string, mark: string, color: string, rgb: string} */
Expand Down
2 changes: 1 addition & 1 deletion packages/realtime-compiler/src/Routing/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function handle(): Response
{
// Media files are always static assets, so we proxy them
// directly without paying for booting the application.
if (str_starts_with($this->request->path, '/media/')) {
if (AssetFileLocator::isMediaPath($this->request->path)) {
return $this->proxyStatic();
}

Expand Down
44 changes: 44 additions & 0 deletions packages/realtime-compiler/tests/RealtimeCompilerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use Hyde\RealtimeCompiler\Http\ExceptionHandler;
use Desilva\Microserve\HtmlResponse;
use Hyde\RealtimeCompiler\Http\HttpKernel;
use Hyde\RealtimeCompiler\Http\DashboardController;
use Hyde\Support\Filesystem\MediaFile;
use Hyde\RealtimeCompiler\Routing\PageRouter;
use Hyde\RealtimeCompiler\Routing\Router;

Expand Down Expand Up @@ -128,6 +130,48 @@ public function testNormalizesMediaPath()
Filesystem::unlink('_media/test.css');
}

public function testHandlesRoutesStaticAssetsInCustomMediaDirectory()
{
putenv('HYDE_SERVER_MEDIA_DIRECTORY=_custom-media');
putenv('HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY=custom-media');

$this->mockCompilerRoute('custom-media/test.css');
Filesystem::ensureDirectoryExists('_custom-media');
Filesystem::put('_custom-media/test.css', 'test');

try {
$kernel = new HttpKernel();
$response = $kernel->handle(new Request());

$this->assertSame(200, $response->statusCode);
$this->assertSame('test', $response->body);
} finally {
putenv('HYDE_SERVER_MEDIA_DIRECTORY');
putenv('HYDE_SERVER_MEDIA_OUTPUT_DIRECTORY');

Filesystem::deleteDirectory('_custom-media');
}
}

public function testDashboardMediaPreviewLinksUseTheConfiguredMediaOutputDirectory()
{
$this->mockCompilerRoute('dashboard');

Hyde::setMediaDirectory('_custom-media');
Filesystem::ensureDirectoryExists('_custom-media');
Filesystem::put('_custom-media/test.css', 'test');

try {
$dashboard = new DashboardController(new Request());

$this->assertSame('/custom-media/test.css', $dashboard->getMediaPreviewLink(MediaFile::make('test.css')));
} finally {
Hyde::setMediaDirectory('_media');

Filesystem::deleteDirectory('_custom-media');
}
}

public function testStaticDirectoryTakesPrecedenceForMediaPath(): void
{
$this->mockCompilerRoute('media/static.jpg');
Expand Down
Loading