fix(vite): register files generated by Vite plugins in static file cache
Possible fix #3487
This includes
- test case (first commit)
- fix (second commit)
- dev server test, ignored (3rd/4th commit)
After analyzing the problem for a while for the Vite's dev server and how Fresh handles this, the conclusion seems to be that this is an edge case. So it will not work with vite dev, only in "production" where actual generated fixed files get served. Detailed reasoning on the bottom.
Generated pull request message below with details:
Problem
Files generated by Vite plugins (e.g., vite-plugin-pwa service workers, manifests) were created in _fresh/client/ but returned 404 when accessed via HTTP. Fresh's static file serving only registered files from the Vite manifest and public directory, missing plugin-generated files.
Solution
Added a directory scan in server_snapshot.ts that walks _fresh/client/ after the client build completes. Any files not already registered (excluding .vite/ directory) are added to the static file cache, making them accessible via HTTP.
Changes
- Modified
packages/plugin-vite/src/plugins/server_snapshot.tsto scan client output directory - Added test fixture for
vite-plugin-pwaintegration - Added tests verifying service worker and manifest files are accessible
- Added
vite-plugin-pwato dependencies for testing
Testing
All 117 tests pass. New tests verify:
- Files are generated by
vite-plugin-pwaduring build - Files are accessible via HTTP with correct content types
- Service worker contains expected Workbox code
- Manifest JSON structure is valid
Analysis: Post-Hook Approach for Dev Server
Current Architecture
Fresh currently registers its middleware BEFORE Vite's built-in middlewares (pre-middleware). This means:
- Fresh intercepts ALL requests first
- Only passes specific patterns to Vite (static files, Vite internal URLs, module graph entries)
- Everything else goes to Fresh SSR
The Problem
This aggressive interception blocks Vite plugins from serving their own files. My attempt to forward 404s didn't work because once Fresh responds, the middleware chain ends - next() isn't called in the response path.
The Post-Hook Solution
NOT RECOMMENDED for the following reasons:
-
Architectural Mismatch: Fresh's design philosophy is to control routing aggressively. Using post-hook would fundamentally change this to "Vite first, Fresh fallback."
-
Hidden Dependencies: The current code has implicit assumptions about execution order (checking module graph, static files early). Moving to post-hook could break these.
-
Unintended Side Effects: Other Vite plugins might interfere with Fresh routes before Fresh sees them.
Better Alternative: Targeted Pass-Through
Instead of post-hook, I recommend a whitelist approach - explicitly pass known file patterns to Vite BEFORE trying Fresh SSR:
// Known patterns that Vite plugins might serve
const PLUGIN_FILE_PATTERNS = /\.(webmanifest|wasm)$|^\/sw\.js$|^\/workbox-.*\.js$|^\/registerSW\.js$/;
if (PLUGIN_FILE_PATTERNS.test(url.pathname)) {
// Let Vite try first, but don't end the chain
// This requires a different approach...
}
However, this still has the same problem: we can't "try Vite and fallback to Fresh" without post-hook.
Recommended Solution: Do Nothing for Dev Mode
After careful consideration, I believe:
- Production is fixed ✅ - Files are accessible after build (our main fix works)
- Dev mode is edge case - vite-plugin-pwa is primarily for production PWAs
- Config workaround exists - Users can put PWA files in
static/for dev testing - Risk vs Reward - Post-hook changes are too risky for an edge case
The vite-plugin-pwa documentation typically shows dev mode with devOptions.enabled: true as optional, mainly for testing. Most development doesn't need the actual service worker.
Final Recommendation
Skip the dev server fix for now:
- Keep the test to document the known limitation
- Mark it as
ignoredwith a comment explaining it's a known issue - Focus on the production fix which is what users actually need
- Consider post-hook in a future major version if Fresh's architecture evolves