Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
cc5dca1
Make the media directory a passthrough for all file types
emmadesilva Aug 28, 2026
f130b42
Stream media file copies instead of loading them into memory
emmadesilva Aug 28, 2026
09eeb0a
Skip resolving media assets that are not referenced in the HTML
emmadesilva Aug 28, 2026
1e864d8
Document the media directory passthrough change
emmadesilva Aug 28, 2026
2cccb63
Trim redundant comments in the passthrough directory change
emmadesilva Aug 28, 2026
085c3d5
Attempt every media file before reporting copy failures
emmadesilva Aug 28, 2026
fa8d16a
Harden and extend media build tests
emmadesilva Aug 28, 2026
dbb4974
Reuse mockFileFinder for the ignored-filename test
emmadesilva Aug 28, 2026
fedb667
Fix docs for the media directory passthrough change
emmadesilva Aug 28, 2026
445eaf4
Serve exactly the media files that the build publishes
claude Oct 7, 2026
80b4ec1
Describe _media as public site assets and strengthen the upgrade warning
claude Oct 7, 2026
851f10c
Mark the media publication rule as internal and drop narrating comments
claude Oct 7, 2026
eb41674
Keep the _media documentation proportional to the user-facing change
claude Oct 7, 2026
0e3429a
Remove media_extensions from the config before updating dependencies
claude Oct 7, 2026
548e06e
Drop the _static and _media collision note from the upgrade guide
claude Oct 7, 2026
3d6c567
List the removed MediaFile::EXTENSIONS constant in the changelog
claude Oct 7, 2026
384fe41
Report media copies that throw instead of aborting the transfer
claude Oct 7, 2026
c0e2816
Refuse to resolve media files that are not published as assets
claude Oct 7, 2026
af3271b
Avoid overstating that every file in _media is published
claude Oct 7, 2026
5368e1f
Regenerate the HydeKernel docs for the asset() PHPDoc change
claude Oct 8, 2026
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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This serves two purposes:
- Pages with non-HTML output paths are now excluded from automatic navigation by default. Explicit navigation front matter now takes precedence over automatic navigation exclusions.
- Fenced code blocks are now rendered through the publishable `components/markdown/code-block.blade.php` view, which changes the generated markup around the code. Syntax highlighting is unaffected, and the `hyde-code-block` and `hyde-code-block-label` classes are stable hooks for your own CSS.
- Code block labels are now set with a `title="…"` modifier on the fence, replacing the `// filepath:` comment syntax, which is no longer recognized and must be replaced.
- The `_media` directory is now a passthrough: files are published to the built site regardless of file type, not just those with a recognized extension. Review `_media` before upgrading, as anything stored there is now public.

### Deprecated
- for changes that will be removed in upcoming releases.
Expand All @@ -38,6 +39,7 @@ This serves two purposes:
- Removed the `rebuild` command. It had no remaining internal consumers now that the realtime compiler renders pages in-memory, and single-page builds can silently leave aggregate outputs (sitemap, RSS, search index, navigation) stale. Use `Hyde\Framework\Actions\StaticPageBuilder::handle()` instead if you need to build a single page programmatically.
- Removed the `InMemoryPage` instance macro API. Use a contents closure for dynamic output, or extend `InMemoryPage` to add custom methods and behavior.
- Removed the `components/filepath-label.blade.php` view, as the label markup now lives in the code block view. If this was published, port any customizations into `components/markdown/code-block.blade.php`.
- Removed the `hyde.media_extensions` config option and the `MediaFile::EXTENSIONS` constant, now that the media directory discovers files of every type.

### Fixed
- Fixed Markdown-to-plain-text conversion consuming unrelated content after ATX headings and stripping literal trailing hashes in https://github.com/hydephp/develop/pull/2590
Expand Down
3 changes: 3 additions & 0 deletions HYDEPHP_V3_PLANNING.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Having this document in code lets us know the devlopment state at any given poin

The undocumented `hyde.empty_output_directory` option is removed too. It was never present in the shipped `config/hyde.php` and never documented, so no upgrade step is needed for it.

- The `_media` directory is now a passthrough for every file it contains, rather than only files matching the `hyde.media_extensions` allow-list. That option is removed, along with `MediaFile::EXTENSIONS`, with no deprecated compatibility constant. `Thumbs.db` and `desktop.ini` are still skipped regardless of case, since Symfony Finder's dotfile and VCS exclusions were already relied on and remain unchanged. This lets media types Hyde never had an opinion about — PDFs, fonts, archives — pass through without a config change, at the cost of also copying files a project may have parked in `_media` for other reasons. The publication rule lives in `MediaFile::isPublishable()`, which the build, `hyde serve`, and `Hyde::asset()` all use, so the preview server no longer serves `_media` files the build skips, such as `Thumbs.db` or dotfiles, and `Hyde::asset()` refuses to link to them.

### Upgrade guide

Please fill in UPGRADE.md as you make changes.
Expand All @@ -92,6 +94,7 @@ Please fill in UPGRADE.md as you make changes.
- Compare a few pages against your old site if you have custom CSS for code blocks or their labels, since the generated markup changed. The `hyde-code-block` and `hyde-code-block-label` classes are stable hooks to target instead of the markup structure.
- Port any customizations from a published `filepath-label.blade.php` to `markdown/code-block.blade.php`. The old file is ignored after upgrading, so the site renders with the shipped label until they are moved.
- Move manually maintained files out of the output directory and into `_static`, since the whole output directory is now emptied before every build. Remove `safe_output_directories` from a published `config/hyde.php`.
- Remove `media_extensions` from a published `config/hyde.php` before updating Composer dependencies, since it references the removed `MediaFile::EXTENSIONS` constant, and move any non-asset files out of `_media` that you were relying on the extension allow-list to keep out of the build.

## `InMemoryPage` content-source motivation

Expand Down
10 changes: 10 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ git commit -m "Pre-upgrade backup before HydePHP v3.0"

//

>warning If your published `config/hyde.php` has a `media_extensions` entry, delete it **before** updating your Composer dependencies. The entry references the removed `MediaFile::EXTENSIONS` constant, so the application fails to load, including during `composer update`, until it is gone. Files in `_media` are no longer filtered by extension, so the option has no replacement. See [Step 13](#step-13-review-the-contents-of-your-media-directory).

### Update Node Dependencies

HydePHP v3 upgrades the bundled `vite` dependency from v7 to v8. Update your `package.json` `devDependencies` to require the new major version:
Expand Down Expand Up @@ -439,10 +441,17 @@ Move those files into the `_static` directory, which is copied verbatim to the s

The `hyde.safe_output_directories` option no longer exists, and the build no longer asks for confirmation before emptying an unfamiliar output directory. Delete the entry from your `config/hyde.php`. Take the chance to double-check your `hyde.output_directory` if you build somewhere other than `_site`, since everything in that directory is now removed on every build.

## Step 13: Review the Contents of Your Media Directory

`_media` is now a passthrough directory: files are no longer filtered by extension, and are published to `_site/media` regardless of their type.

>warning This changes what you deploy. **Review the contents of `_media` before upgrading.** Any normal file stored there, such as design source files, notes, exports, or source maps, will now be publicly accessible on your built site. Move anything that should not be public out of `_media`.

## Migration Checklist

Use this checklist to track your upgrade progress:

- [ ] Removed any `media_extensions` entry from `config/hyde.php` before updating Composer dependencies
- [ ] Reviewed `markdown.allow_html` and `markdown.enable_blade` and explicitly selected the appropriate trust policy
- [ ] Replaced any `php hyde rebuild <path>` usage with `StaticPageBuilder::handle()` or a full `php hyde build`
- [ ] Moved calls to `Redirect::create()` or `Redirect::store()` into the `hyde.redirects` configuration array
Expand All @@ -456,6 +465,7 @@ Use this checklist to track your upgrade progress:
- [ ] Compared pages against your old site if you have custom CSS for code blocks or their labels
- [ ] Checked `_posts` for drafts and blog posts dated in the future, and set up recurring builds if scheduling posts
- [ ] Moved manually maintained files out of the output directory and into `_static`
- [ ] Reviewed `_media` and moved out anything that shouldn't be public

## Troubleshooting

Expand Down
4 changes: 0 additions & 4 deletions config/hyde.php
Original file line number Diff line number Diff line change
Expand Up @@ -483,10 +483,6 @@
|
*/

// Change the file extensions to be considered as media files and are copied to the output directory.
// If you want to add more extensions, add it to the empty merge array, or just override the entire array.
'media_extensions' => array_merge([], \Hyde\Support\Filesystem\MediaFile::EXTENSIONS),

// Should a JSON build manifest with metadata about the build be generated?
'generate_build_manifest' => true,

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<section id="hyde-kernel-hyperlink-methods">

<!-- Start generated docs for Hyde\Foundation\Concerns\ForwardsHyperlinks -->
<!-- Generated by HydePHP DocGen script at 2024-09-08 10:25:34 in 0.11ms -->
<!-- Generated by HydePHP DocGen script at 2026-10-08 11:22:40 in 0.05ms -->

#### `formatLink()`

Expand All @@ -27,7 +27,7 @@ No description provided.
Hyde::asset(string $name): Hyde\Support\Filesystem\MediaFile
```

- **Throws:** \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory.
- **Throws:** \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory, or is not published to the site.

#### `url()`

Expand Down
4 changes: 2 additions & 2 deletions docs/architecture-concepts/the-hydekernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ Hyde::markdown(string $text, bool $normalizeIndentation): Illuminate\Support\Htm
<section id="hyde-kernel-hyperlink-methods">

<!-- Start generated docs for Hyde\Foundation\Concerns\ForwardsHyperlinks -->
<!-- Generated by HydePHP DocGen script at 2024-09-08 10:25:34 in 0.11ms -->
<!-- Generated by HydePHP DocGen script at 2026-10-08 11:22:40 in 0.05ms -->

#### `formatLink()`

Expand All @@ -227,7 +227,7 @@ No description provided.
Hyde::asset(string $name): Hyde\Support\Filesystem\MediaFile
```

- **Throws:** \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory.
- **Throws:** \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory, or is not published to the site.

#### `url()`

Expand Down
5 changes: 3 additions & 2 deletions docs/creating-content/managing-assets.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ Here you will find the `app.css` file that bootstraps the TailwindCSS styles. Th
to add your custom styles. It is also where we import HydeFront. If you compile this file in the base install,
it will output the same file that's already included in Hyde.

- The `_media` folder contains **compiled** (and usually minified) files. When Hyde compiles your static site,
all asset files here will get copied as they are into the `_site/media` folder.
- The `_media` folder contains your public site assets, such as images, stylesheets, scripts, fonts, and downloads.
When Hyde compiles your static site, files here are copied as-is into the `_site/media` folder, regardless of file type. This is also
where Vite writes the compiled (and usually minified) styles and scripts from `resources/assets`.

- The `_site/media` folder contains the files that are served to the user.

Expand Down
11 changes: 0 additions & 11 deletions docs/digging-deeper/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,17 +354,6 @@ Learn more in the [Documentation Pages](documentation-pages#sidebar) documentati
The following configuration options in the `config/hyde.php` file are intended for advanced users and
should only be modified if you fully understand their impact. The code examples show the default values.

### `media_extensions`

This option allows you to specify file extensions considered as media files, which will be copied to the output directory.
To add more extensions, either append them to the existing array or override the entire array.

```php title="config/hyde.php"
use \Hyde\Support\Filesystem\MediaFile;

'media_extensions' => array_merge([], MediaFile::EXTENSIONS),
```

### `generate_build_manifest`

Determines whether a JSON build manifest with metadata about the build should be generated. Set to `true` to enable.
Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/core-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ To take full advantage of the framework, it may first be good to familiarize our
| `_docs` | For documentation pages |
| `_posts` | For blog posts |
| `_pages` | For static Markdown and Blade pages |
| `_media` | Store static assets to be copied to the build directory |
| `_media` | Public site assets copied as-is to the build directory |
| `_site` | The build directory where your compiled site will be stored |
| `config` | Configuration files for Hyde and integrations |
| `resources/assets` | Location for CSS/JS Vite source files (optional) |
Expand Down Expand Up @@ -72,7 +72,7 @@ All source and output directories are configurable, but the defaults are as foll
| Static Pages | `_pages/` | `_site/` | `.md`, `.blade.php` |
| Blog Posts | `_posts/` | `_site/posts/` | `.md` |
| Documentation | `_docs/` | `_site/docs/` | `.md` |
| Media Assets | `_media/` | `_site/media/` | Common asset types |
| Media Assets | `_media/` | `_site/media/` | Any file type |

## Paths, Identifiers, and Route Keys

Expand Down
4 changes: 0 additions & 4 deletions packages/framework/config/hyde.php
Original file line number Diff line number Diff line change
Expand Up @@ -483,10 +483,6 @@
|
*/

// Change the file extensions to be considered as media files and are copied to the output directory.
// If you want to add more extensions, add it to the empty merge array, or just override the entire array.
'media_extensions' => array_merge([], \Hyde\Support\Filesystem\MediaFile::EXTENSIONS),

// Should a JSON build manifest with metadata about the build be generated?
'generate_build_manifest' => true,

Expand Down
2 changes: 1 addition & 1 deletion packages/framework/src/Facades/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Asset
/**
* Get a MediaFile instance for the given filename in the media source directory.
*
* @throws \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory.
* @throws \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory, or is not published to the site.
*/
public static function get(string $file): MediaFile
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function relativeLink(string $destination): string
return $this->hyperlinks->relativeLink($destination);
}

/** @throws \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory. */
/** @throws \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory, or is not published to the site. */
public function asset(string $name): MediaFile
{
return $this->hyperlinks->asset($name);
Expand Down
7 changes: 2 additions & 5 deletions packages/framework/src/Foundation/Concerns/HasMediaFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Hyde\Foundation\Concerns;

use Hyde\Hyde;
use Hyde\Facades\Config;
use Hyde\Facades\Filesystem;
use Hyde\Support\Filesystem\MediaFile;
use Illuminate\Support\Collection;
Expand Down Expand Up @@ -38,13 +37,11 @@ protected static function discoverMediaFiles(): Collection
$file = MediaFile::make($path);

return [$file->getIdentifier() => $file];
});
})->filter(fn (MediaFile $file, string $identifier): bool => MediaFile::isPublishable($identifier));
}

protected static function getMediaFiles(): array
{
return Filesystem::findFiles(Hyde::getMediaDirectory(),
Config::getArray('hyde.media_extensions', MediaFile::EXTENSIONS), recursive: true
)->all();
return Filesystem::findFiles(Hyde::getMediaDirectory(), recursive: true)->all();
}
}
2 changes: 1 addition & 1 deletion packages/framework/src/Foundation/Kernel/Hyperlinks.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public function relativeLink(string $destination): string
* If a base URL is configured, the image will be returned with a qualified absolute URL.
* Otherwise, a relative path will be returned based on the rendered page's location.
*
* @throws \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory.
* @throws \Hyde\Framework\Exceptions\FileNotFoundException If the file does not exist in the `_media` source directory, or is not published to the site.
*/
public function asset(string $name): MediaFile
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@

namespace Hyde\Framework\Actions\PreBuildTasks;

use ErrorException;
use RuntimeException;
use Hyde\Facades\Config;
use Hyde\Facades\Filesystem;
use Hyde\Support\Filesystem\MediaFile;
use Symfony\Component\Console\Command\Command;
use Hyde\Framework\Features\BuildTasks\PreBuildTask;
use Hyde\Framework\Concerns\InteractsWithDirectories;

use function implode;
use function array_map;

class TransferMediaAssets extends PreBuildTask
{
protected static string $message = 'Transferring Media Assets';
Expand All @@ -30,13 +36,34 @@ public function handle(): void
$this->skip("No media files to transfer.\n");
}

$this->withProgressBar($files, function (MediaFile $file): void {
$failures = [];

$this->withProgressBar($files, function (MediaFile $file) use (&$failures): void {
$sitePath = $file->getOutputPath();
$this->needsParentDirectory($sitePath);
Filesystem::putContents($sitePath, $file->getContents());
$transfer = "[{$file->getPath()}] to [{$sitePath}]";

try {
$this->needsParentDirectory($sitePath);
$copied = Filesystem::copy($file->getPath(), $sitePath);
} catch (ErrorException $exception) {
$failures[] = "$transfer: {$exception->getMessage()}";

return;
}

if (! $copied) {
$failures[] = $transfer;
}
});

$this->newLine();

if ($failures !== []) {
throw new RuntimeException(
"Failed to copy media file(s):\n".implode("\n", array_map(fn (string $failure): string => "- $failure", $failures)),
Command::FAILURE
);
}
}

public function printFinishMessage(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public static function postprocess(string $html): string
}

foreach (static::assetMap() as $sourcePath => $mediaFile) {
if (! str_contains($html, $sourcePath)) {
continue;
}

$patterns = [
sprintf('<img src="%s"', $sourcePath),
sprintf('<img src="/%s"', $sourcePath),
Expand Down
Loading
Loading