Repository navigation
Conversation
…file `renderRoutesForProdStatic` wrote each file of a page with `Bun.write` and kept no record of the paths. A static page and a param page that `getParams` fills in to the same path both wrote `dist/<path>/index.html`. The build printed `done` and exited with code 0. One page was lost. The write loop now claims each output file before the first write of the page. A second claim by a page of another route, or by a page of the same route with other params, fails the build and names the routes and the file. A route that yields the same params again renders the same page again, and that file is not written again.
|
Updated 3:50 AM PT - Oct 30th, 2026
❌ @robobun, your commit fde3a2a has 1 failures in
🧪 To try this PR locally: bunx bun-pr 47357That installs a local version of the PR into your bun-47357 --bun |
|
Status: ready for review. Stacked on #47356: merge that one first. CI is red. The one test that failed is a test of main that this PR does not change. Two builds of this tree each failed on one test of
Why this is not from this PR:
I will not push again to start CI. In each of the two builds, every test lane on Linux (with the ASAN lane) and on Windows passed, and so did How I reproduced it (debug build of main 11c3671, linux x64):
One decision is for a maintainer: the build now exits with code 1 when two pages generate the same output file. #47146 asks the same question for route pattern collisions. |
…bobun/cc3f1bce/bake-prerender-output-collision
…bobun/cc3f1bce/bake-prerender-output-collision
…bobun/cc3f1bce/bake-prerender-output-collision
…put file
The error for two pages of one route on one output file has the params of the
two pages, in string order:
Route "routes/[id].ts" generates the output file "x/index.html" more than
once: for the params {"id":"./x"} and {"id":"x"}.
The error for two routes sorts its two source files with `sort()`.
…bobun/cc3f1bce/bake-prerender-output-collision
`JSON.stringify` throws for a BigInt and for a cycle, and the build then failed with that TypeError and not with the clash. `Bun.inspect` with `compact` gives one line for each value.
Stacked on #47356.
Problem
bun build --appwrites two pages to one output file with no message.routes/first.tsandroutes/[id].tswith{ id: "first" }both writedist/first/index.html. The build exits with code 0.renderRoutesForProdStatic(src/js/builtins/Bake.ts:96) callsBun.writefor each file and records no path. The route scan sees no clash:/firstand/:iddiffer.Fix
Mapbefore the page's first write. A second claim fails the build:error: Routes "routes/[id].ts" and "routes/first.ts" both generate the output file "first/index.html".test/bake/dev/production.test.ts(output files): 16 cases, and each fails without this change.Background
/index.html).bun buildandnext build(Pages Router) fail such builds.Downsides
..in a param.Notes
Where it came from. No user reported this. Another session found it by reading the code (the body of #47146 lists it under "Not in this PR"). The #bake Discord channel was not searched.
--appneeds a canary build or the feature flag.Repro. A framework with no dependency, on a debug build of main 11c3671:
bun build --app ./app.ts: exit code 0,done, no error line. In 200 runsdist/first/index.htmlheld the param page 198 times and the static page 2 times. A file watcher shows two writes to that path: a pool thread writes the static page, then the main thread writes the param page. The same happens for two param routes ([id]witha,[...rest]with["a"]), a param with a slash (a/bbesideroutes/a/b.ts), an empty param besideindex.ts, an empty catch-all besidedocs/index.ts, and with the built-in React framework (pages/first.tsxbesidepages/[id].tsxwhosegetStaticPathsyieldsfirst).With this PR each of them exits with code 1 and prints the error line. The second page is not written.
The rule.
Mapis the string that goes toBun.write:path.join(outBase, filled route path + file name). So every prerender write goes through the one check, for each router type, eachgetParamsform, and each file name of the framework.error: Route "routes/[id].ts" generates the output file "x/index.html" more than once: for the params { id: "./x" } and { id: "x" }.The two params are in string order, because the two pages can arrive in either order.Bun.inspectformats them, so a BigInt or a cycle in the params does not hide the error.error: Route "routes/index.ts" generates the output file "index.html" more than once.Bun.deepEquals): the page is rendered again, and a file it already has is not written again. AgetStaticPathsthat returns a path twice builds as before. The first shape of this PR compared the filled route path and not the params. The review showed that it then kept one of two different pages without a message, and not the same one in each run with{ pages }.Why the write loop. File names exist only after the render, so no earlier place sees every clash. The other designs that the design review weighed:
FrameworkRouter::match_slow). It needs the first host function in bake, and the unsorted dynamic matcher gives a narrow route's URLs to a catch-all by hash order, which fails valid builds.Bun.writeor in the file system.dist/is not emptied between builds, so an existing file is not a clash.Policy.
bun build:Multiple files share the same output pathfor entry points and chunks (src/bundler/linker_context/generateChunksInParallel.rs:444,src/bundler/transpiler.rs:2757), also when the bytes are equal. It does not check copied assets (bundler: add copied assets to the duplicate output path check #46655 is open for that).next build, Pages Router:detectConflictingPathsinpackages/next/src/build/utils.tsfails the build. Inside one page it keeps the first entry of a repeated path and drops the rest before the render (build/static-paths/pages.ts). Here a page gets its params object, so only equal params count as the same page.prerenderConflictBehavior: 'warn'). Bake has no priority between two param routes (router/mod.rs: "TODO: no code to sort this data structure")./first. The static build now fails on the same tree. A failure can become a warning later without breaking a build. The reverse cannot.Exit codes. 102 probe builds, each built 3 to 5 times on the base (#47356) and on this PR:
\w, such as[cat-item]. The fill onBake.ts:108reads:cat-itemas:cat, so their pages share one path. On the base those builds exit 0 and lose pages. Now they fail on the clash. The fill is not changed here. bake: consolidate the open robobun dev-server, HMR runtime, production build and router fixes #39488 builds the path from route parts.Cost on a build with no clash. The base is #47356. Debug builds, linux x64.
JS2NATIVE_COUNT425 to 425, noJSC_DEFINE_HOST_FUNCTIONinsrc/runtime/bake/*.cpp).heapStats().objectTypeCountsover 30 and over 60 pages, JIT off.) The path is filled in once per page and not once per file.Mapget and 1 set per output file: +1.3k with the JIT off, +0.6k with it (instructions:u, 1e6 paths of 52 characters, release 1.4.3).getParamsreturns an array, +141 B to +231 B when it is a generator (the claim then keeps each params object). Heap size after a full GC at the last page, 500 to 4000 pages, 1 and 2 files per page.getParamsrequests,prerendercalls andBun.writecalls, and the microtask turns between them: the same in the threegetParamsforms (100 pages, 1 and 2 files per page).Tests. Block
output files, with a framework that has no dependency.test.concurrent.each. Each expects the one error line, nodone, exit code 1, and the files ofdist. In each case the second page waits until the first page is on disk, so the files ofdistare exact.getParamsform, one route path with two file names, two routes on one route path with different file names, and a page with no files. Between the builds the test changes each file indist.bun build --appwhen a page fails to prerender #47356) all 16 fail. With this PR all 16 pass.Not in this PR. Each of these still exits with code 0.
bun build.dist/maps to one file.dist/(client chunks,production/mod.rs:720)...in a param or a file name writes outsidedist/.about.tsbesideabout/index.ts) prints an error and still exits 0 until bake: failbun build --appwhen two files match the same route pattern #47146.Later. The rule is a rule of
renderRoutesForProdStatic, the writer ofbun build --app. #46018 (bun bake build) was closed "while the Bake design is reworked". A new writer needs its own rule.Self-review. The first shape was one PR. The review found 30 concerns, 21 about this part. Changed: the split into two PRs, equal params as the test for the same page, the comment on what a clashing page writes, the JSDoc, three test gaps (a page written by the async iterable arm, a repeat with
{ pages }and other params, a second build over changed files), and this text. Not done: no issue was opened for the items under "Not in this PR".After the first push. A review comment asked for the params in the error for two pages of one route (
REVIEW.md: an error message echoes the rejected value). The error has them now. A second comment showed thatJSON.stringifythrows for a BigInt or a cycle in the params, soBun.inspectformats them. The error for two routes is not changed: it names both source files and the output file.no test proof · iteration 5 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/bake/dev/production.test.ts