HydePHP media copying in v3 (Deep Research)
Abstract
HydePHP has historically used a configurable list of file extensions to determine which files are copied from the _media directory to _site/media.
For HydePHP v3, I want to reconsider whether that allow-list still provides enough value to justify its complexity. Since _media is already a directory specifically intended for public assets, should Hyde simply copy everything placed there?
A possible middle ground would be to copy all files while excluding a small set of known junk or placeholder files, such as .gitkeep, .DS_Store, Thumbs.db, and AppleDouble ._* files.
This document evaluates the costs and benefits of each approach and recommends the behavior that makes the most sense for HydePHP v3.
Background
In HydePHP v2 and earlier, Hyde used MediaFile::EXTENSIONS to determine which files in _media qualified as media files and should be copied into _site/media.
The default extension list contained:
png, svg, jpg, jpeg, webp, gif, ico, css, js
Users could extend or replace this list through the hyde.media_extensions configuration option.
This provided a clear definition of what Hyde considered a media file, but it also meant that files outside the configured list were silently excluded from the generated site. Legitimate static assets such as PDFs, fonts, JSON files, web manifests, source maps, and downloadable archives therefore required additional configuration.
HydePHP v3 is currently being developed on the master branch. This gives us an opportunity to reconsider the old behavior without introducing a breaking change into an existing stable release.
Recommendation
For HydePHP v3, _media should become a true passthrough directory:
Recursively copy all files from _media to _site/media.
Exclude a small set of known junk and placeholder files.
Allow users to configure additional exclusion patterns.
Stop using MediaFile::EXTENSIONS as the primary rule for deciding which files are copied.
The directory itself should define publication intent:
Everything placed in _media is public unless it is explicitly excluded.
There was value in the extension constant, particularly as a guardrail against accidentally publishing unrelated files. However, that value was limited in a directory already dedicated to publishable assets. The allow-list also introduced recurring costs through unexpected behavior, documentation requirements, configuration discovery, and maintenance whenever new asset types became common.
Existing user expectations
Hyde’s documentation has consistently described _media as the location for static assets that are copied into the generated site.
This creates a straightforward user expectation:
Put a file in _media, and Hyde will publish it under _site/media.
An extension allow-list weakens that contract. A user can place a valid asset in the correct directory and still find that it was not copied because Hyde did not recognize its extension.
That behavior is especially surprising for common static assets such as:
The user should not need to discover an advanced configuration option before Hyde will copy an otherwise ordinary static file.
Comparable public-directory models
The closest comparison to Hyde’s _media directory is the public or static directory used by other static-site generators.
Tools such as Astro and Hugo treat these directories as passthrough locations. Files placed there are copied into the generated site without being restricted to a predefined set of extensions.
This works because the directory name already communicates the contract: its contents are intended for publication.
Jekyll takes a more exclusion-oriented approach because it processes a broader source tree containing both public files and internal project files. It therefore needs additional include and exclude rules.
Hyde’s _media directory is already isolated from the rest of the project. It does not need the same defensive filtering as a generator that scans a mixed source directory.
Eleventy requires users to configure passthrough copy rules explicitly. Hyde already provides _media as a built-in convention, so requiring additional declarations would add configuration without improving the common case.
For Hyde, the Astro and Hugo model is the most relevant: a dedicated public directory should copy arbitrary files by default.
Cost-benefit analysis
Option 1: Keep the extension allow-list
Under this option, Hyde would continue copying only files whose extensions appear in MediaFile::EXTENSIONS or hyde.media_extensions.
Benefits
Prevents many unrelated files from being published accidentally.
Automatically filters common operating-system metadata and placeholder files.
Provides a narrow and explicit definition of what Hyde considers media.
Preserves existing behavior and configuration semantics.
Costs
Legitimate static assets are silently ignored unless their extensions are configured.
Users must learn about an advanced option to publish ordinary files.
The default list requires maintenance as frontend asset types evolve.
Documentation must explain that _media does not necessarily copy everything.
The behavior conflicts with the natural interpretation of a dedicated public-assets directory.
Extension-based file discovery adds implementation complexity.
A file’s extension is not always a reliable indicator of whether it should be public.
The allow-list provides protection, but it optimizes for accidental misuse of _media rather than its intended use.
Option 2: Copy everything
Under this option, Hyde would recursively copy every file from _media into _site/media.
Benefits
Provides the simplest possible mental model.
Supports all current and future asset types automatically.
Matches the documented purpose of _media.
Removes the need to maintain a media-extension registry.
Eliminates configuration for common files such as PDFs and fonts.
Produces behavior consistent with other dedicated public-directory systems.
Makes the implementation easier to understand and test.
Costs
Placeholder files such as .gitkeep may be published.
Operating-system metadata such as .DS_Store and Thumbs.db may be published.
Users who treat _media as general storage may unintentionally expose files.
Version-control metadata could be copied if it somehow exists inside _media.
This option has the strongest developer experience, but publishing obvious junk files would make the generated output unnecessarily messy.
Option 3: Copy everything with explicit exclusions
Under this option, Hyde would recursively copy all files except those matching a small exclusion list.
Benefits
Preserves the simple passthrough-directory model.
Supports arbitrary asset types without additional configuration.
Prevents common junk and placeholder files from being published.
Allows advanced users to exclude project-specific files or directories.
Avoids maintaining an ever-growing registry of valid extensions.
Makes publication rules explicit and easy to explain.
Better aligns the implementation with the purpose of _media.
Costs
Requires a small amount of exclusion-matching logic.
The default exclusion list must be maintained.
An obscure junk file not included in the defaults could still be copied.
Changes to the existing configuration model require migration documentation.
This option provides most of the simplicity of copying everything while preserving the useful protection offered by the old allow-list.
Comparison
Option | Developer experience | Legitimate asset support | Junk-file protection | Maintenance burden | Ecosystem fit
-- | -- | -- | -- | -- | --
Keep the extension allow-list | Low | Medium | High | High | Low
Copy everything | High | High | Low | Low | High
Copy everything with exclusions | High | High | High enough | Low to medium | High
Copying everything with a small explicit exclusion list provides the best overall balance.
Proposed v3 behavior
HydePHP v3 should:
Treat _media as a recursive passthrough directory.
Copy all files regardless of extension.
Skip a small default set of known junk files.
Introduce a configurable exclusion list, such as hyde.media_exclude.
Deprecate or remove hyde.media_extensions as a file-discovery mechanism.
A possible default configuration could look like this:
'media_exclude' => [
'.gitkeep',
'.DS_Store',
'Thumbs.db',
'._*',
'.git/**',
'.hg/**',
'.svn/**',
],
The exact pattern syntax would need to be defined, but it should support both filenames and recursive paths.
Default exclusions
The default list should remain intentionally small.
Good candidates include:
.gitkeep
.DS_Store
Thumbs.db
._*
.git/
.hg/
.svn/
These files have little or no legitimate purpose in generated website output.
Hyde should not exclude every dotfile by default. Unlike a general project source directory, _media is specifically intended for publication. Some hidden files may be intentionally public, and excluding all of them would recreate the same surprise caused by the extension allow-list.
The rule should therefore be:
Exclude known junk, not broad categories of potentially valid files.
Why .gitignore should not control publication
Hyde could technically reuse .gitignore rules when deciding which media files to copy, but this should not be the default.
.gitignore describes source-control policy. It does not necessarily describe publication policy.
A file may be excluded from Git because it is generated locally while still needing to be published. Conversely, a file may be tracked in Git but intentionally excluded from the generated site.
Using a dedicated media_exclude setting keeps these concerns separate and makes Hyde’s behavior easier to understand.
Support for .gitignore-aware exclusions could be introduced later as an explicit opt-in feature, but it should not define the default behavior.
Validation behavior
Changing _media to a passthrough directory does not require weakening Hyde’s asset validation.
Hyde can continue validating explicitly referenced assets and throwing a helpful FileNotFoundException when a referenced file does not exist.
These are separate concerns:
Allowing arbitrary file types in _media only broadens publication support. It does not prevent Hyde from reporting missing assets clearly.
Implementation outline
The implementation can remain simple:
Recursively enumerate files under the configured media directory.
Normalize each path relative to the _media root.
Test the relative path against the default and configured exclusions.
Create the corresponding parent directory under _site/media.
Copy each remaining file.
Laravel’s filesystem utilities or Symfony Finder can both support this model cleanly.
The implementation should avoid extension-based glob patterns. Direct recursive enumeration followed by explicit filtering is easier to understand, more portable, and easier to test.
Suggested tests
The v3 test suite should verify that:
Existing image, CSS, and JavaScript files continue to copy.
Files outside the old allow-list are copied.
Nested files preserve their directory structure.
Default junk files are skipped.
Custom exclusion rules work recursively.
Legitimate dotfiles can still be published.
Empty directories do not need to be reproduced unless explicitly required.
Explicitly referenced missing assets continue to produce helpful errors.
Example files should include:
docs/manual.pdf
fonts/app.woff2
data/site.json
manifest.webmanifest
downloads/example.zip
nested/assets/example.txt
Default exclusion tests should include:
.gitkeep
.DS_Store
Thumbs.db
._logo.png
.git/config
.hg/store/example
.svn/entries
Migration strategy
The cleanest v3 migration would be to replace extension-based discovery entirely.
Documentation should describe _media in plain language:
All files in _media are copied to _site/media, except files matching the configured exclusion rules.
Any documentation presenting media_extensions as the normal way to enable additional static files should be removed or updated.
There are two possible compatibility approaches.
Clean break
Remove hyde.media_extensions from copy discovery in v3.
This is the clearest option and is appropriate for a major release. Existing projects would generally gain support for more files rather than lose functionality.
The main observable difference would be that previously ignored files might now be copied unless explicitly excluded.
Transitional deprecation
Continue reading hyde.media_extensions temporarily while emitting a deprecation notice explaining that _media now uses passthrough semantics.
This provides a softer migration path, but it may also make the behavior harder to explain. It would need to be clear whether the extension setting still filters files, is ignored, or is translated into another rule.
Unless there is a significant compatibility concern, the clean break is preferable.
Conclusion
MediaFile::EXTENSIONS provided a useful guardrail in earlier versions of HydePHP, but it is not the best default publication model for v3.
Because _media is already a dedicated public-assets directory, the most intuitive rule is that files placed there should be published. Restricting publication by extension creates more friction and maintenance than value.
HydePHP v3 should therefore copy all files from _media recursively while excluding a small, explicit set of known junk and placeholder files. Additional exclusions should be configurable through a dedicated setting.
This gives Hyde a simple and durable contract:
Everything in _media is public unless explicitly excluded.
HydePHP media copying in v3 (Deep Research)
Abstract
HydePHP has historically used a configurable list of file extensions to determine which files are copied from the
_mediadirectory to_site/media.For HydePHP v3, I want to reconsider whether that allow-list still provides enough value to justify its complexity. Since
_mediais already a directory specifically intended for public assets, should Hyde simply copy everything placed there?A possible middle ground would be to copy all files while excluding a small set of known junk or placeholder files, such as
.gitkeep,.DS_Store,Thumbs.db, and AppleDouble._*files.This document evaluates the costs and benefits of each approach and recommends the behavior that makes the most sense for HydePHP v3.
Background
In HydePHP v2 and earlier, Hyde used
MediaFile::EXTENSIONSto determine which files in_mediaqualified as media files and should be copied into_site/media.The default extension list contained:
Users could extend or replace this list through the
hyde.media_extensionsconfiguration option.This provided a clear definition of what Hyde considered a media file, but it also meant that files outside the configured list were silently excluded from the generated site. Legitimate static assets such as PDFs, fonts, JSON files, web manifests, source maps, and downloadable archives therefore required additional configuration.
HydePHP v3 is currently being developed on the
masterbranch. This gives us an opportunity to reconsider the old behavior without introducing a breaking change into an existing stable release.Recommendation
For HydePHP v3,
_mediashould become a true passthrough directory:Recursively copy all files from
_mediato_site/media.Exclude a small set of known junk and placeholder files.
Allow users to configure additional exclusion patterns.
Stop using
MediaFile::EXTENSIONSas the primary rule for deciding which files are copied.The directory itself should define publication intent:
There was value in the extension constant, particularly as a guardrail against accidentally publishing unrelated files. However, that value was limited in a directory already dedicated to publishable assets. The allow-list also introduced recurring costs through unexpected behavior, documentation requirements, configuration discovery, and maintenance whenever new asset types became common.
Existing user expectations
Hyde’s documentation has consistently described
_mediaas the location for static assets that are copied into the generated site.This creates a straightforward user expectation:
An extension allow-list weakens that contract. A user can place a valid asset in the correct directory and still find that it was not copied because Hyde did not recognize its extension.
That behavior is especially surprising for common static assets such as:
PDF documents
Font files such as
.woff,.woff2, and.ttfWeb manifests
JSON data files
Source maps
Text files
Downloadable archives
The user should not need to discover an advanced configuration option before Hyde will copy an otherwise ordinary static file.
Comparable public-directory models
The closest comparison to Hyde’s
_mediadirectory is the public or static directory used by other static-site generators.Tools such as Astro and Hugo treat these directories as passthrough locations. Files placed there are copied into the generated site without being restricted to a predefined set of extensions.
This works because the directory name already communicates the contract: its contents are intended for publication.
Jekyll takes a more exclusion-oriented approach because it processes a broader source tree containing both public files and internal project files. It therefore needs additional include and exclude rules.
Hyde’s
_mediadirectory is already isolated from the rest of the project. It does not need the same defensive filtering as a generator that scans a mixed source directory.Eleventy requires users to configure passthrough copy rules explicitly. Hyde already provides
_mediaas a built-in convention, so requiring additional declarations would add configuration without improving the common case.For Hyde, the Astro and Hugo model is the most relevant: a dedicated public directory should copy arbitrary files by default.
Cost-benefit analysis
Option 1: Keep the extension allow-list
Under this option, Hyde would continue copying only files whose extensions appear in
MediaFile::EXTENSIONSorhyde.media_extensions.Benefits
Prevents many unrelated files from being published accidentally.
Automatically filters common operating-system metadata and placeholder files.
Provides a narrow and explicit definition of what Hyde considers media.
Preserves existing behavior and configuration semantics.
Costs
Legitimate static assets are silently ignored unless their extensions are configured.
Users must learn about an advanced option to publish ordinary files.
The default list requires maintenance as frontend asset types evolve.
Documentation must explain that
_mediadoes not necessarily copy everything.The behavior conflicts with the natural interpretation of a dedicated public-assets directory.
Extension-based file discovery adds implementation complexity.
A file’s extension is not always a reliable indicator of whether it should be public.
The allow-list provides protection, but it optimizes for accidental misuse of
_mediarather than its intended use.Option 2: Copy everything
Under this option, Hyde would recursively copy every file from
_mediainto_site/media.Benefits
Provides the simplest possible mental model.
Supports all current and future asset types automatically.
Matches the documented purpose of
_media.Removes the need to maintain a media-extension registry.
Eliminates configuration for common files such as PDFs and fonts.
Produces behavior consistent with other dedicated public-directory systems.
Makes the implementation easier to understand and test.
Costs
Placeholder files such as
.gitkeepmay be published.Operating-system metadata such as
.DS_StoreandThumbs.dbmay be published.Users who treat
_mediaas general storage may unintentionally expose files.Version-control metadata could be copied if it somehow exists inside
_media.This option has the strongest developer experience, but publishing obvious junk files would make the generated output unnecessarily messy.
Option 3: Copy everything with explicit exclusions
Under this option, Hyde would recursively copy all files except those matching a small exclusion list.
Benefits
Preserves the simple passthrough-directory model.
Supports arbitrary asset types without additional configuration.
Prevents common junk and placeholder files from being published.
Allows advanced users to exclude project-specific files or directories.
Avoids maintaining an ever-growing registry of valid extensions.
Makes publication rules explicit and easy to explain.
Better aligns the implementation with the purpose of
_media.Costs
Requires a small amount of exclusion-matching logic.
The default exclusion list must be maintained.
An obscure junk file not included in the defaults could still be copied.
Changes to the existing configuration model require migration documentation.
This option provides most of the simplicity of copying everything while preserving the useful protection offered by the old allow-list.
Comparison
Option | Developer experience | Legitimate asset support | Junk-file protection | Maintenance burden | Ecosystem fit -- | -- | -- | -- | -- | -- Keep the extension allow-list | Low | Medium | High | High | Low Copy everything | High | High | Low | Low | High Copy everything with exclusions | High | High | High enough | Low to medium | HighCopying everything with a small explicit exclusion list provides the best overall balance.
Proposed v3 behavior
HydePHP v3 should:
Treat
_mediaas a recursive passthrough directory.Copy all files regardless of extension.
Skip a small default set of known junk files.
Introduce a configurable exclusion list, such as
hyde.media_exclude.Deprecate or remove
hyde.media_extensionsas a file-discovery mechanism.A possible default configuration could look like this:
The exact pattern syntax would need to be defined, but it should support both filenames and recursive paths.
Default exclusions
The default list should remain intentionally small.
Good candidates include:
These files have little or no legitimate purpose in generated website output.
Hyde should not exclude every dotfile by default. Unlike a general project source directory,
_mediais specifically intended for publication. Some hidden files may be intentionally public, and excluding all of them would recreate the same surprise caused by the extension allow-list.The rule should therefore be:
Why
.gitignoreshould not control publicationHyde could technically reuse
.gitignorerules when deciding which media files to copy, but this should not be the default..gitignoredescribes source-control policy. It does not necessarily describe publication policy.A file may be excluded from Git because it is generated locally while still needing to be published. Conversely, a file may be tracked in Git but intentionally excluded from the generated site.
Using a dedicated
media_excludesetting keeps these concerns separate and makes Hyde’s behavior easier to understand.Support for
.gitignore-aware exclusions could be introduced later as an explicit opt-in feature, but it should not define the default behavior.Validation behavior
Changing
_mediato a passthrough directory does not require weakening Hyde’s asset validation.Hyde can continue validating explicitly referenced assets and throwing a helpful
FileNotFoundExceptionwhen a referenced file does not exist.These are separate concerns:
Passthrough copying determines which files are eligible to be published.
Asset validation determines whether an explicitly referenced file exists.
Allowing arbitrary file types in
_mediaonly broadens publication support. It does not prevent Hyde from reporting missing assets clearly.Implementation outline
The implementation can remain simple:
Recursively enumerate files under the configured media directory.
Normalize each path relative to the
_mediaroot.Test the relative path against the default and configured exclusions.
Create the corresponding parent directory under
_site/media.Copy each remaining file.
Laravel’s filesystem utilities or Symfony Finder can both support this model cleanly.
The implementation should avoid extension-based glob patterns. Direct recursive enumeration followed by explicit filtering is easier to understand, more portable, and easier to test.
Suggested tests
The v3 test suite should verify that:
Existing image, CSS, and JavaScript files continue to copy.
Files outside the old allow-list are copied.
Nested files preserve their directory structure.
Default junk files are skipped.
Custom exclusion rules work recursively.
Legitimate dotfiles can still be published.
Empty directories do not need to be reproduced unless explicitly required.
Explicitly referenced missing assets continue to produce helpful errors.
Example files should include:
Default exclusion tests should include:
Migration strategy
The cleanest v3 migration would be to replace extension-based discovery entirely.
Documentation should describe
_mediain plain language:Any documentation presenting
media_extensionsas the normal way to enable additional static files should be removed or updated.There are two possible compatibility approaches.
Clean break
Remove
hyde.media_extensionsfrom copy discovery in v3.This is the clearest option and is appropriate for a major release. Existing projects would generally gain support for more files rather than lose functionality.
The main observable difference would be that previously ignored files might now be copied unless explicitly excluded.
Transitional deprecation
Continue reading
hyde.media_extensionstemporarily while emitting a deprecation notice explaining that_medianow uses passthrough semantics.This provides a softer migration path, but it may also make the behavior harder to explain. It would need to be clear whether the extension setting still filters files, is ignored, or is translated into another rule.
Unless there is a significant compatibility concern, the clean break is preferable.
Conclusion
MediaFile::EXTENSIONSprovided a useful guardrail in earlier versions of HydePHP, but it is not the best default publication model for v3.Because
_mediais already a dedicated public-assets directory, the most intuitive rule is that files placed there should be published. Restricting publication by extension creates more friction and maintenance than value.HydePHP v3 should therefore copy all files from
_mediarecursively while excluding a small, explicit set of known junk and placeholder files. Additional exclusions should be configurable through a dedicated setting.This gives Hyde a simple and durable contract: