-
-
Notifications
You must be signed in to change notification settings - Fork 115
Description
Summary
When building with deploy: 'vercel', the preload/loader JS files are generated with different CACHE_KEY values than the client entry JS bundle. This causes some client-side navigations to fail on Vercel production because the browser requests preload files that don't exist, receives HTML instead, and rejects them due to MIME type mismatch.
Environment
oneversion: 1.4.11- Deploy target: Vercel (
web.deploy: 'vercel') - Build tool: bun
- OS: macOS (local), Linux (Vercel CI)
Reproduction
- Create a project with
oneusingdeploy: 'vercel'and at least one SSG route - Run
bun run build - Inspect the generated
.vercel/output/static/assets/directory:
# Entry JS has one CACHE_KEY:
grep -oE 'ch="[0-9]+"' .vercel/output/static/assets/_virtual_one-entry-*.js
# => ch="31971176"
# Preload files have DIFFERENT CACHE_KEYs:
ls .vercel/output/static/assets/ | grep '_preload\.' | grep -oE '_[0-9]+_preload' | sort -u
# => _3375520_preload
# => _7325102_preload
# => _28556756_preload
# => _32467716_preload
# => ... (none match 31971176)- Deploy to Vercel — clicking some internal link results in a white blank page
Symptoms on Vercel Production
Browser console shows:
Failed to load module script: Expected a JavaScript-or-Wasm module script but the server
responded with a MIME type of "text/html". Strict MIME type checking is enforced for module
scripts per HTML spec.
[one] preload error for /ko/privacy-policy: TypeError: Failed to fetch dynamically imported
module: https://example.com/assets/ko_privacy-policy_5481575_preload.js
All _preload.js, _preload_css.js, and _vxrn_loader.js files return text/html because they don't exist on the CDN — the actual files have different numeric keys in their names.
Root Cause
In constants.ts:
export const CACHE_KEY = `${process.env.ONE_CACHE_KEY ?? Math.round(Math.random() * 100_000_000)}`The build uses worker threads by default for parallel page building (see workerPool.ts / buildPageWorker.ts). Each worker thread imports constants.ts independently and generates its own random CACHE_KEY via Math.random().
Meanwhile, the main Vite build process compiles the client entry JS with the main process's CACHE_KEY (via Vite define in one.ts):
'process.env.ONE_CACHE_KEY': JSON.stringify(CACHE_KEY),This define only affects the client bundle (compile-time replacement). It does not set the actual process.env.ONE_CACHE_KEY environment variable, so worker threads never see it.
Result
| Component | CACHE_KEY | Source |
|---|---|---|
| Client entry JS (browser) | A |
Main process Math.random(), inlined by Vite define |
| Worker 1 (preload files) | B |
Worker's own Math.random() |
| Worker 2 (preload files) | C |
Worker's own Math.random() |
| Worker N... | N |
Each worker's own Math.random() |
The client requests /assets/ko_privacy-policy_A_preload.js but the file on disk is /assets/ko_privacy-policy_B_preload.js.
Why it works with bun run serve but not Vercel
In oneServe.ts, the serve middleware has a graceful fallback for missing preload files:
if (c.req.path.endsWith(PRELOAD_JS_POSTFIX)) {
if (!preloads[c.req.path]) {
c.header('Content-Type', 'text/javascript')
c.status(200)
return c.body(``) // empty JS — no error
}
}On Vercel, there's no such middleware — the missing file falls through to the catch-all rewrite route in config.json, which rewrites to / and returns the root HTML page. The browser then rejects HTML as a JavaScript module.
Suggested Fix
Set process.env.ONE_CACHE_KEY in the main process before spawning worker threads, so all workers inherit the same value:
In build.ts, before the worker pool is initialized:
// Ensure all workers share the same CACHE_KEY
process.env.ONE_CACHE_KEY = CACHE_KEYOr alternatively, pass CACHE_KEY to workers via postMessage and have buildPageWorker.ts set process.env.ONE_CACHE_KEY before importing constants.ts.
Workaround
Set the ONE_CACHE_KEY environment variable explicitly before building:
ONE_CACHE_KEY=my_stable_key bun run buildFor Vercel, in vercel.json:
{
"buildCommand": "ONE_CACHE_KEY=$VERCEL_GIT_COMMIT_SHA bun run build"
}This ensures all processes and worker threads use the same cache key.