You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feature #81 Add a per-entry opt-out of copied filenames hashing (pyrech)
This PR was merged into the main branch.
Discussion
----------
Add a per-entry opt-out of copied filenames hashing
| Q | A
| -------------- | ---
| Bug fix? | no
| New feature? | yes <!-- please update CHANGELOG.md file -->
| Deprecations? | no <!-- if yes, also update UPGRADE-*.md and CHANGELOG.md files -->
| Documentation? | yes <!-- required for new features, or documentation updates -->
| Issues | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
| License | MIT
Encore's `copyFiles()` let projects keep stable physical paths for copied files
(e.g. `to: 'images/[path][name].[ext]?[hash:8]'`: verbatim path on disk, hash in
the query string). Large codebases rely on that contract: templates referencing
copied files by a hardcoded `asset('/build/images/logo.svg')` without going
through the manifest, Twig filters or PHP code reading them from a predictable
location on disk, CDN path rules…
Reprise's `copy` option always content-hashes the emitted filenames, which breaks those references and currently forces such projects into a custom plugin. While migrating a large Symfony site (5 sites, 800+ templates, ~300 hardcoded asset paths) from Encore to Reprise, we ended up maintaining a 75-line Vite plugin just to reproduce the Encore behavior.
This PR adds a per-entry `hash` option (default `true`, current behavior
unchanged). When `false`, the file keeps its logical path on disk and the content hash moves to the `manifest.json` value as a query string, so cache-busting through `asset()` keeps working:
```js
Symfony({
copy: [
{ from: 'assets/images', to: 'images', hash: false },
],
})
```
```
{ "build/images/logo.svg": "/build/images/logo.svg?87dcc351" }
```
Dev-server behavior is unchanged (files were already copied verbatim, and dev manifest values stay unversioned).
Two design notes:
- Per entry rather than global, so hashed and stable copies can be mixed in
one config.
- The hash still lands in the manifest value rather than disappearing: it is
Encore's historical contract, and asset() consumers keep per-file cache-busting. The trade-off (proxies/CDNs configured to ignore query strings
will not pick up new versions) is documented, which is why hashed filenames remain the default.
The PR also makes an empty `to: ''` copy files at the root of `outputPath`
(previously producing a leading-slash fileName that Rollup rejects) — needed for files like `favicon.ico` or `site.webmanifest` that must live at a fixed top-level path.
We verified the feature end-to-end on the migration mentioned above: swapping the custom plugin for `hash: false` entries produces byte-identical trees and manifests in build, and the same on-disk copies + manifest entries in dev.
Commits
-------
75181d6 Add a per-entry opt-out of copied filenames hashing
Copy file name to clipboardExpand all lines: CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,10 @@
1
1
# CHANGELOG
2
2
3
+
## 0.8.0
4
+
5
+
- Add a per-entry `hash` option to `copy` (default `true`): with `hash: false` the file is emitted at its logical path instead of a content-hashed one, and the hash moves to the `manifest.json` value as a `?<contenthash>` query string
6
+
- Support an empty `to` on a `copy` entry, emitting the files at the root of `outputPath`
7
+
3
8
## 0.7.0
4
9
5
10
- Add the `RenderAssetTagEvent`, dispatched before each rendered `<script>`/`<link>` (including the injected dev client and React preamble), so listeners can add, change or remove attributes such as a CSP nonce
0 commit comments