Reprise 0.8.0 is out, a few weeks after the project was introduced on this blog. This release brings back an Encore behavior that some codebases depend on: copied files that keep a stable path on disk. It also fixes a manifest discrepancy between Vite and Rsbuild.
Copied Files Can Keep Their Path
Encore's copyFiles() could keep a copied file at a fixed path on disk and
put the hash in the query string instead. Codebases end up relying on that:
templates hardcoding asset('/build/images/logo.svg'), PHP code reading a
file from a known location, CDN rules. Reprise always hashed the filename, so
migrating such a project from Encore meant writing a custom plugin.
A copy entry now accepts a hash option, true by default. Set it to
false and the file keeps its logical path, while the content hash moves to
the manifest.json value as a query string:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// vite.config.ts
import { defineConfig } from 'vite'
import Symfony from '@symfony/reprise/vite'
export default defineConfig({
// ...
plugins: [
Symfony({
copy: [
{
from: 'assets/images',
to: 'images',
hash: false,
},
],
}),
],
})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// rsbuild.config.ts
import { defineConfig } from '@rsbuild/core'
import Symfony from '@symfony/reprise/rsbuild'
export default defineConfig({
// ...
plugins: [
Symfony({
copy: [
{
from: 'assets/images',
to: 'images',
hash: false,
},
],
}),
],
})
The file lands at public/build/images/logo.svg and the manifest carries the
cache-busting query, so asset() keeps returning a versioned URL:
1
{ "build/images/logo.svg": "/build/images/logo.svg?87dcc351" }
The option is per entry, so hashed and stable copies can live in the same
config. Proxies or CDNs configured to ignore query strings will not pick up new
versions of these files, which is why hashed filenames remain the default. The
same pull request also lets an empty to place copied files at the root of
outputPath, for the ones that have to sit at a fixed top-level URL like
favicon.ico or site.webmanifest.
Same Manifest Keys on Vite and Rsbuild
manifest.json maps a logical asset path to its hashed URL, which is what
makes asset('fonts/query.woff2') resolve to the built file in Twig. A
stylesheet can reference that same file with a query string or a fragment
tacked on, and both are everyday CSS:
1 2 3 4 5 6 7 8
@font-face {
font-family: "Query";
src: url("./fonts/query.woff2?v=1") format("woff2");
}
.icon {
background: url("./media/icon.svg#frag");
}
Vite wrote the manifest key as build/fonts/query.woff2, with the query
string already stripped. Rsbuild carried it over instead and wrote
build/fonts/query.woff2?v=1, a key that asset('fonts/query.woff2') in
Twig would never match. Same project, same CSS: the font resolved through
Vite and failed through Rsbuild. The query string and the fragment describe
how the CSS references the file, not the file itself, so both are now
stripped before the manifest key is written.
Corepack Is Gone
Node.js stopped shipping Corepack in v25, after the TSC voted to stop distributing it. It was also a slow way to reach pnpm, since every command went through a Node.js shim that first had to resolve and download the package manager.
Reprise no longer relies on it. The CI workflows use pnpm/setup, which
fetches the standalone pnpm binary and installs Node.js in one step, and
contributors install pnpm the usual way instead of running corepack enable.
Symfony UX made the same move in symfony/ux#3774.
Full Changelog
- #81 Add a per-entry opt-out of copied filenames hashing (@pyrech)
- #82 [Manifest] Strip
url()query and fragment from Rspack keys (@Kocal) - #84 [CI] Replace Corepack with
pnpm/setup(@Kocal) - #85 [CI] Use
pnpm/setupin the release workflow (@Kocal) - #83 Update all npm devDependencies (@Kocal)
- #79 Link the announcement blog post in the README (@Kocal)
Reprise is still experimental and still 0.x, so this is the moment where feedback shapes what comes after Encore. Try it on a real project, open an issue, or send a pull request on GitHub.