Repository navigation
Conversation
A failure frees the content of the failed file, so a CssChild becomes Unknown. On the next save, invalidate() took the Js/Unknown arm and queued only the file itself. The root that inlines the file was not built again, so the served stylesheet had none of the file's rules until one more save. A failed file now also queues each importer that is a CssRoot, as the CssRoot/CssChild arm does for a healthy child.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review. WalkthroughThe dev-server incremental graph now requeues dependent CSS roots when a client file fails. A regression test covers recovery from syntax errors and missing imports in nested CSS, including hard reload validation. ChangesNested CSS recovery
Suggested reviewers: Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to The CSS recovery change has targeted regression coverage, and no actionable merge risk is identified. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
Status Reproduced on the 1.4.3 canary (09bb546) and on a debug build of main, two ways:
The sibling for the HTML side of the same report is #42741. |
There was a problem hiding this comment.
LGTM — targeted fix that mirrors the adjacent CssRoot | CssChild arm, with a test that exercises both failure modes at two import depths.
What was reviewed:
- The new dependent-walk loop in
invalidateis byte-for-byte the same shape as the existing one at lines 1635-1650 (sameRawSlice-over-key pattern, sameappend_css);entry_pointsis a separate map so appending can't invalidatebundled_fileskeys. - Checked sibling arms:
Assetalready walks dependents,Serverside has no CSS handling, and the healthyCssChildpath already does this — thefailed → Unknowncase was the only gap. - Test uses the
devTestharness with awaited style assertions anddev.writeerror expectations (no sleeps), covers syntax error + unresolved@ import, transitive and direct child, and a hard reload.
Extended reasoning...
Overview
The PR fixes a bake dev-server HMR bug: when a CSS file that a root stylesheet @ imports fails to build and is then fixed, the root was not re-bundled because the failure had reset the child's Content to Unknown, sending the fixing save down the Js | Unknown arm of invalidate, which only queued the file itself. The fix adds a ~20-line block to that arm that, when the file is currently failed, walks its incoming-edge list and queues each CssRoot dependent as a CSS entry point — the exact same loop the CssRoot | CssChild arm already runs for a healthy child. A new devTest in test/bake/dev/css.test.ts sets up a two-level @ import chain, breaks the leaf with a syntax error and the middle file with an unresolved @ import, fixes each, and asserts every rule is present both live and after a hard reload.
Security risks
None. This is internal dev-server incremental-graph bookkeeping — deciding which files to rebundle on a watched-file change. No untrusted input parsing, no auth, no network, no crypto. The RawSlice usage is the same borrow-lifetime pattern already used three times in the same function; the slice points into bundled_files.keys() and is consumed by entry_points.append_css (a separate StringArrayHashMap), so no map growth can invalidate it mid-use.
Level of scrutiny
Low-to-moderate. The Rust change is small, mechanical, and copies an adjacent established pattern verbatim (same edge-list traversal, same RawSlice::new(&*keys()[dep]) idiom, same stale_files.set + append_css pair). The PR description gives a clear root-cause trace with graph-state dumps and explains why depth doesn't matter (a root holds an edge to every file in its chunk) and why both failure modes converge on the same Unknown state. The bug hunt ran to a dry streak with no findings.
Other factors
Neither changed file is covered by CODEOWNERS. The test is added to the existing css.test.ts alongside the related "css import another css file" case, uses the harness's awaited dev.write({ errors }) and c.style(...).expect.toBe(...) primitives (no sleeps, no timeouts), and covers the variant matrix REVIEW.md asks for: both failure kinds, both a direct and a transitive child, and a hard reload to verify the served stylesheet, not just the HMR patch. The PR timeline shows a single commit with no prior reviews or outstanding objections.
|
Updated 12:28 PM PT - Sep 14th, 2026
✅ @robobun, your commit 1e7d3df070fb27c18f2d15e7633adc699c124828 passed in 🧪 To try this PR locally: bunx bun-pr 42740That installs a local version of the PR into your bun-42740 --bun |
|
Closing in favor of #42738. Both PRs change the same arm of
The test from this PR (a file two imports deep, both kinds of failure) is now in #42738 as f43e185. It also saves the repaired file three more times after each repair. |
Problem
@imports is saved broken (syntax error or unresolved@import), then saved fixed. The page answers 200, but the served stylesheet has none of that file's rules ([MAIN_v1], a fresh dev server serves[DEP_v2, MAIN_v1]) until one more save. Reproduces on 1.4.2, canary and main.Content::CssChildbecomesContent::Unknown(insert_failure,src/runtime/bake/dev_server/incremental_graph.rs). On the fixing saveinvalidatetakes theJs | Unknownarm and queues only the file. The root that inlines it is not built again.Fix
failedfile also queues each importer that is aCssRoot, as theCssRoot | CssChildarm does for a healthy child.receive_chunkon a chunk entry retracts a failure, and a CSS child never gets one.test/bake/dev/css.test.ts(fails on canary). Also all oftest/bake/andtest/js/bun/http/bun-serve-html*.test.ts.Background
IncrementalGraphis the dev server's module graph. An edge tells the importer when the imported file changes.@imported file into the root's chunk. Those files areCssChildand have no output of their own.invalidateturns the changed paths from the watcher into the entry points of the next rebuild.Notes
The file is bundled alone on the fixing save and becomes a CSS root of its own. The
CssRootarm walks importers, which is why a second save heals the page.Found by a convergence differential on the dev server (fuzz ledger #51215): after a script of ordinary saves has settled, the page and every stylesheet and script it references are compared with a fresh dev server on the same files.
Graph state from a debug build,
index.htmllinksmain.css,main.csshas@import "./dep.css":Both ways to break the file end in the same state. A syntax error goes through
handle_parse_task_failure->insert_failure. An unresolved@importgoes throughget_log_for_resolution_failures->insert_stale(frees the content) and theninsert_failureinprepare_and_log_resolution_failures.Standalone repro from the report, on a debug build with the fix (
HOW=syntaxgives the same lines):Not changed here:
@importand are bundled together give only the first root an edge. That is dev server: give every CSS root in a bundle its own edges to the files it imports #42697.invalidatehunk that would cover this case as a side effect. It is from August 6, it no longer applies to main, and it has no test for this case. bake: consolidate the open robobun dev-server, HMR runtime, production build and router fixes #39488 does not touchinvalidate.Suites run on a debug+ASAN build with the fix: all of
test/bake/dev/(144 pass; the 5production.test.tscases that time out at the 5 s default pass with--timeout 120000and pass on a release build),test/bake/*.test.ts(62 pass),test/js/bun/http/bun-serve-html*.test.ts(49 pass).