refactor: migrate jumpToState to Astro.licals to prevent build-time state leakage - #1271
refactor: migrate jumpToState to Astro.licals to prevent build-time state leakage#1271riya-saharan wants to merge 4 commits into
Conversation
|
@riya-saharan There is a merge conflict, do try to fix it and I can merge after that. Thanks. |
lirenjie95
left a comment
There was a problem hiding this comment.
Hi @riya-saharan, thanks for the PR! Moving jumpToState from a global singleton to Astro.locals makes sense for build-time isolation.
A few requests:
1. Missing trailing newlines
Most modified files are missing their final newline. Please add them back to reduce diff noise.
2. Link to #1207 and resolve conflicts
Could you update the PR description to include Fixes #1207 (or Closes #1207) and resolve the current merge conflicts with main?
3. Hard-to-reproduce bug needs more confidence
Since the issue in #1207 only appears randomly in production and is difficult to reproduce locally, could you add some extra verification? For example, a short note in the PR description explaining why Astro.locals guarantees per-request isolation during SSG would help reviewers be confident this actually fixes the race condition.
Please let me know once these are addressed. Thanks again!
|
Hi @riya-saharan, just checking in on this PR. Both @limzykenneth and @lirenjie95 left feedback that needs to be addressed, specifically the merge conflicts and the points raised in the May review. Are you still working on this? Please let us know if you need help or are no longer able to continue. Thank you. |
|
Hi @Nwakaego-Ego, thanks for checking in — I am still working on this. Currently resolving the merge conflicts locally and will address the review feedback (trailing newlines, linking #1207, and the isolation explanation) in the same push. Will update here once it's ready for re-review. |
|
Thanks for the review! I've resolved the merge conflicts with v1 and fixed a few build issues that surfaced during the merge (missing imports, a duplicate state declaration). Ran a full production build afterward — 6129 pages built successfully with 0 errors. Fixes #1207 Re: isolation guarantee during SSG — per Astro's own middleware docs: "locals is an object that lives and dies within a single Astro route; when your route page is rendered, locals won't exist anymore and a new one will be created," and this holds "even when this middleware runs at build time." So even with our build.concurrency: 2 setting running pages in parallel, each page gets its own isolated locals object — there's no shared mutable state for concurrent locale builds to race on, unlike the old module-level singleton in state.ts. One thing I want to flag directly: Astro's docs describe locals as intended to be set via middleware, not directly in page/layout frontmatter (see withastro/astro#11174). This PR sets it directly in layouts instead. It works correctly and passes build/preview testing, but wanted to be upfront in case it's worth discussing — happy to move to a middleware-based approach if you'd prefer. Working on the trailing newlines next. |
There was a problem hiding this comment.
Pull request overview
This PR refactors the website’s “Jump To” sidebar state handling to avoid locale cross-contamination during parallel/static builds by moving jumpToState from a global singleton to per-request Astro.locals.
Changes:
- Migrated jump-to state storage from
src/globals/state.tsruntime globals toAstro.localswithApp.Localstyping insrc/env.d.ts. - Updated pages/layouts/components to set/read
Astro.locals.jumpToStateand removed mostjumpToStateprop plumbing throughBaseLayout/Nav. - Removed the global
jumpToStatevariable andsetJumpToStatesetter, leaving only the shared types.
Reviewed changes
Copilot reviewed 30 out of 32 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/index.astro | Clears Astro.locals.jumpToState for the homepage route. |
| src/pages/education-resources/index.astro | Sets page-specific Astro.locals.jumpToState links for education resources. |
| src/pages/about.astro | Clears Astro.locals.jumpToState for about route. |
| src/pages/[locale]/index.astro | Clears Astro.locals.jumpToState for localized homepage route. |
| src/pages/[locale]/education-resources/index.astro | Sets localized education-resources jump-to links via Astro.locals. |
| src/pages/[locale]/about.astro | Clears Astro.locals.jumpToState for localized about route. |
| src/layouts/TutorialsLayout.astro | Sets tutorials page jump-to links into Astro.locals and stops passing prop. |
| src/layouts/TutorialLayout.astro | Stores generated jump-to state in Astro.locals and removes prop passing. |
| src/layouts/TextDetailLayout.astro | Clears Astro.locals.jumpToState and removes jumpToState={null} prop. |
| src/layouts/SketchLayout.astro | Clears Astro.locals.jumpToState and removes jumpToState={null} prop. |
| src/layouts/SketchesLayout.astro | Clears Astro.locals.jumpToState and removes jumpToState={null} prop. |
| src/layouts/SearchLayout.astro | Clears Astro.locals.jumpToState and removes jumpToState={null} prop. |
| src/layouts/ReferenceLayout.astro | Sets reference directory jump-to state into Astro.locals and removes prop. |
| src/layouts/ReferenceItemLayout.astro | Sets per-reference-item jump-to state into Astro.locals and removes prop. |
| src/layouts/PeopleLayout.astro | Clears Astro.locals.jumpToState and removes jumpToState={null} prop. |
| src/layouts/LibrariesLayout.astro | Sets libraries jump-to state into Astro.locals and removes prop. |
| src/layouts/HomepageLayout.astro | Clears Astro.locals.jumpToState and removes jumpToState={null} prop. |
| src/layouts/ExamplesLayout.astro | Sets examples jump-to state into Astro.locals and removes prop. |
| src/layouts/ExampleLayout.astro | Stores generated jump-to state in Astro.locals and removes prop passing. |
| src/layouts/EventsLayout.astro | Clears Astro.locals.jumpToState and removes jumpToState={null} prop. |
| src/layouts/EventLayout.astro | Clears Astro.locals.jumpToState and removes jumpToState={null} prop. |
| src/layouts/ContributorDocLayout.astro | Sets contributor-doc headings jump-to state into Astro.locals and removes prop. |
| src/layouts/ContributeLayout.astro | Sets contribute page jump-to state into Astro.locals and removes prop. |
| src/layouts/CommunityLayout.astro | Sets community page jump-to state into Astro.locals and removes prop. |
| src/layouts/BaseLayout.astro | Writes jumpToState prop into Astro.locals and renders <Nav /> without props. |
| src/layouts/AboutLayout.astro | Sets about page jump-to state into Astro.locals and removes global setter usage. |
| src/globals/state.ts | Removes global mutable state + setter; keeps only shared types. |
| src/env.d.ts | Adds App.Locals.jumpToState typing for Astro.locals. |
| src/components/Settings/index.astro | Reads jump-to state from Astro.locals instead of global singleton. |
| src/components/Nav/NavPanels.tsx | Adjusts JumpToState type import path. |
| src/components/Nav/index.astro | Reads jump-to state from Astro.locals and passes it to NavPanels. |
| package-lock.json | Lockfile metadata churn included alongside refactor changes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@lirenjie95 @Nwakaego-Ego — this should be ready for another look now. Conflicts resolved, trailing newlines fixed, PR description updated with the isolation explanation, and I also addressed Copilot's two review comments. Full production build passes (6129 pages, 0 errors). Let me know if anything else is needed! |
|
@lirenjie95 — this should be ready for another look now. Conflicts resolved, trailing newlines fixed, PR description updated with the isolation explanation, and I also addressed Copilot's two review comments. Full production build passes (6129 pages, 0 errors). Would appreciate a re-review when you get a chance! |
lirenjie95
left a comment
There was a problem hiding this comment.
Thanks for the PR. A few notes:
- #1391 / #1454 already fixed the same core issue by passing
jumpToStateas a prop, but that fix is incomplete:AboutLayout, the homepage pages, andeducation-resourcesstill use the globalsetJumpToState, and the global variable is still exported fromsrc/globals/state.ts. - Your
Astro.localsapproach is more thorough, but it conflicts with the prop-based pattern already merged in #1391. - Please revert the
package-lock.jsonchanges — they are unrelated noise. - The implementation is inconsistent:
BaseLayoutstill takes ajumpToStateprop while other layouts write directly toAstro.locals. Pick one pattern. - Setting
Astro.locals.jumpToState = nullin pages only to have the layout overwrite it is fragile.
Recommendation: close this and open a smaller PR that completes #1391's prop-based fix by converting the remaining setJumpToState calls and removing the global state. If the project prefers Astro.locals, this PR needs significant cleanup first.
This PR addresses the issue of state leakage across different locales during parallel static builds.
The Problem:
Previously, the jumpToState (used for the "Jump To" sidebar) was managed via a global singleton in src/globals/state.ts. During high-concurrency builds, pages from different locales (e.g., Korean and Chinese) would overwrite this global variable, leading to incorrect translations appearing on the wrong pages.
The Solution:
I have migrated the state management to Astro.locals, which provides an isolated data store for every unique request/page build.
Key Changes:
State Isolation: Moved jumpToState into App.Locals via env.d.ts.
Component Updates: Refactored Nav.astro, NavPanels.tsx, and Settings.tsx to consume state from the request context.
Thread-Safety: Removed the global variable and setter from state.ts.
Lifecycle Management: Updated all layouts and localized page entry points to explicitly set or reset their own jumpToState within the Astro.locals scope.
Verification:
Successfully passed npm run build with 0 errors. Verified language isolation in a local preview environment across multiple localized routes.