Problem
The /upload-assets endpoint and the render endpoint both copy ALL uploaded files into ALL target bundle directories, including bundle JS files that don't belong in a given directory.
When RSC support is enabled, the Ruby side sends both the server bundle and the RSC bundle along with assets (like loadable-stats.json, react-client-manifest.json). The targetBundles array contains both bundle hashes. The Node side blindly copies every uploaded file into every target directory.
Actual result (observed by running rake react_on_rails_pro:copy_assets_to_remote_vm_renderer)
.node-renderer-bundles/
├── 6b9a6c...-development/ ← server bundle dir
│ ├── 6b9a6c...-development.js ← server bundle (correct, 8.2 MB)
│ ├── 81b7b6...-development.js ← RSC bundle (EXTRA, 4.7 MB)
│ ├── loadable-stats.json ← correct
│ ├── react-client-manifest.json ← correct
│ ├── react-server-client-manifest.json ← correct
│ └── .react-on-rails-node-renderer-bundle-completed
│
├── 81b7b6...-development/ ← RSC bundle dir
│ ├── 6b9a6c...-development.js ← server bundle (EXTRA, 8.2 MB)
│ ├── 81b7b6...-development.js ← RSC bundle (correct, 4.7 MB)
│ ├── loadable-stats.json ← correct
│ ├── react-client-manifest.json ← correct
│ ├── react-server-client-manifest.json ← correct
│ └── .react-on-rails-node-renderer-bundle-completed
Expected result
Each directory should only contain its own bundle JS file plus the shared assets:
.node-renderer-bundles/
├── 6b9a6c...-development/
│ ├── 6b9a6c...-development.js ← only the server bundle
│ ├── loadable-stats.json
│ ├── react-client-manifest.json
│ ├── react-server-client-manifest.json
│ └── .react-on-rails-node-renderer-bundle-completed
│
├── 81b7b6...-development/
│ ├── 81b7b6...-development.js ← only the RSC bundle
│ ├── loadable-stats.json
│ ├── react-client-manifest.json
│ ├── react-server-client-manifest.json
│ └── .react-on-rails-node-renderer-bundle-completed
Impact
- Wasted disk space: ~13 MB extra in the example above (each bundle duplicated in the other's directory). In production with larger bundles this could be significant.
- Wasted I/O: copying multi-MB files that will never be read.
- Functionally harmless: the renderer only loads the JS file matching the directory name, so the extra files are ignored.
Root cause
The /upload-assets endpoint at worker.ts:327 treats every uploaded file uniformly:
const assets: Asset[] = Object.values(req.body).filter(isAsset);
Then at worker.ts:372, copies ALL of them into each target directory:
await copyUploadedAssets(assets, bundleDirectory);
The endpoint has no concept of "this bundle belongs to this directory." It doesn't parse the bundle_ prefix from form field keys like the render endpoint does.
The same issue exists in the render endpoint at handleRenderRequest.ts:205-206, where the same assetsToCopy array (which includes assets but not bundles) is passed to every handleNewBundleProvided call — though in that case bundles are handled separately via moveUploadedAsset.
Possible fix
On the Node side, the /upload-assets endpoint could filter files per target bundle: for each targetBundle, only copy the matching bundle_{hash} file plus the non-bundle assets (assetsToCopy*). This would require parsing the form field key names (like the render endpoint already does for bundle and bundle_* keys).
Problem
The
/upload-assetsendpoint and the render endpoint both copy ALL uploaded files into ALL target bundle directories, including bundle JS files that don't belong in a given directory.When RSC support is enabled, the Ruby side sends both the server bundle and the RSC bundle along with assets (like
loadable-stats.json,react-client-manifest.json). ThetargetBundlesarray contains both bundle hashes. The Node side blindly copies every uploaded file into every target directory.Actual result (observed by running
rake react_on_rails_pro:copy_assets_to_remote_vm_renderer)Expected result
Each directory should only contain its own bundle JS file plus the shared assets:
Impact
Root cause
The
/upload-assetsendpoint atworker.ts:327treats every uploaded file uniformly:Then at
worker.ts:372, copies ALL of them into each target directory:The endpoint has no concept of "this bundle belongs to this directory." It doesn't parse the
bundle_prefix from form field keys like the render endpoint does.The same issue exists in the render endpoint at
handleRenderRequest.ts:205-206, where the sameassetsToCopyarray (which includes assets but not bundles) is passed to everyhandleNewBundleProvidedcall — though in that case bundles are handled separately viamoveUploadedAsset.Possible fix
On the Node side, the
/upload-assetsendpoint could filter files per target bundle: for eachtargetBundle, only copy the matchingbundle_{hash}file plus the non-bundle assets (assetsToCopy*). This would require parsing the form field key names (like the render endpoint already does forbundleandbundle_*keys).