Repository navigation
refactor(router): remove dead merged route trie/entries, share the flattened map - #753
Merged
Merged
Conversation
4 tasks done
…attened map The global merged route trie was dead: its only reader was manifest-init's own write-guard, while find-match.ts matches exclusively on per-router tries (including build-time prerender via matchForPrerender). Yet every production build and every dev discovery/HMR pass ran a full second O(total-routes) trie construction to produce it. The merged precomputedEntries global was equally dead (dev-only writer; the '?? getPrecomputedEntries()' fallback was unreachable because every discovered router gets a per-router array), and mergedRouteTrailingSlash existed only to feed the dead merged build. Removed: cachedRouteTrie/setRouteTrie/getRouteTrie, cachedPrecomputedEntries/setPrecomputedEntries/getPrecomputedEntries, the merged-trie build block in discover-routers, state.mergedRouteTrie/ mergedPrecomputedEntries, the propagateDiscoveryState global pushes, and the dead buildRouteToStaticPrefix re-export (pre-release API hygiene: removed, not deprecated; no external importers). Also: the eager routes-manifest codegen now materializes each router's flattened name->path map ONCE and shares the object between setCachedManifest and setRouterManifest — __flat previously ran twice per router on cold start and a single-router app held two byte-identical maps. Sharing is safe: all consumers read these maps; lazy-include deltas merge into globalRouteMap, a different object.
ivogt
force-pushed
the
refactor/drop-dead-merged-route-data
branch
from
July 10, 2026 17:42
f12e9d8 to
a13d69a
Compare
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #752 (base branch
fix/cf-dev-stale-authoritative-trie; GitHub retargets to main when it merges).Problem statement
The route-data audit that followed #751 found that the global merged route data is dead weight:
find-match.ts— the only runtime matcher, including build-time prerender viamatchForPrerender— reads exclusively per-router tries (find-match.ts:73), yet every production build and every dev discovery/HMR pass ran a full second O(total-routes) trie construction (discover-routers.ts:301-346) whose product nothing ever consumed. The global trie's only reader wasmanifest-init.ts:49— which read it solely to decide whether to write it. The comment justifying the build-time push ("needed for matchForPrerender") was factually wrong: the per-router install directly below it is what prerender collection matches on. Same story for the merged precomputedEntries (dev-only writer, unreachable??fallback atrouter.ts:409since every discovered router gets a per-router array —[]is truthy) andmergedRouteTrailingSlash(existed only to feed the dead merged build).Separately, the eager routes-manifest module ran
__flat()twice per router — once forsetCachedManifest, once forsetRouterManifest— producing two byte-identical maps; a single-router app held its full route map twice, and the second transform ran on every cold start. The exact disease #751 removed from the lazy module, one module over.No consumer-visible change: no public API surface is used by any app/test/example (verified by repo-wide grep; only re-exports removed under the pre-release remove-don't-deprecate rule), and
reverse()/href()/matching behavior is identical.What was removed
cachedRouteTrie+setRouteTrie/getRouteTriediscover-routers.tsbuild push,propagateDiscoveryStatedev push,manifest-init.tsself-guarded writerouteToStaticPrefix/prerender/passthrough/responseType collection +buildRouteTriecall)discover-routers.ts:301-346cachedPrecomputedEntries+set/getPrecomputedEntriespropagateDiscoveryState;?? getPrecomputedEntries()fallback inrouter.tsmergedRouteTrailingSlash,state.mergedRouteTrie,state.mergedPrecomputedEntriesbuildRouteToStaticPrefixre-export inmanifest-utils.tsroute-trie.ts)The
__flatsharing (before vs. after, generated eager module, single router)Before:
After:
Multi-router: one
const __mNper router, shared intosetRouterManifest, spread into thesetCachedManifest({...})union. Sharing safety was verified against every consumer ofgetGlobalRouteMap()/getRouterManifest()(all read-only; lazy-include deltas mutateglobalRouteMap, a different object; the handler self-heal replacescachedManifestwith a new object rather than mutating). Pinned by a codegen test asserting exactly one map materialization.What this buys
Build/HMR latency: one full trie construction + O(routes) prefix-map pass removed per production build and per dev discovery pass (tens of ms at the 26k-route reference scale, noise at small scale). Cold start: one O(routes)
__flatpass and one duplicate map allocation removed per router. Memory: single-router apps hold one flattened map instead of two. Maintenance: three writers no longer need to keep a value "correct" that nobody reads, and the staleensureRouterManifestcomment claiming a "global merged trie" fallback (find-match never had one — the real fallback is regex over live routes) is corrected. Net −132 lines.Code-change summary
src/route-map-builder.ts— global trie/precomputed slots + 4 accessors deleted;clearAllRouterDatano longer clears them;ensureRouterManifestcomment corrected.src/vite/discovery/discover-routers.ts— merged-trie build block and merged-entries accumulation deleted; per-router build and RSC-realm per-router install (which prerender collection actually uses) untouched.src/vite/discovery/virtual-module-codegen.ts— per-router map consts (__m<i>) shared between both setters;genFileVarByEntryreplaces the dual-counter indexing; stale merged-trie comment updated.src/vite/router-discovery.ts—propagateDiscoveryStatedrops the two global pushes (per-router loop unchanged).src/rsc/manifest-init.ts,src/server.ts,src/router.ts— dead imports/exports/fallback removed.AGENTS.mdBundle Hygiene rule Router idea 2 revisit #1 +docs/manifests.md— reworded off the deleted identifiers (contract unchanged: route data in exactly one lazy chunk).Test plan
pnpm run typecheck,pnpm run test:unit:all,pnpm run lint,pnpm run formatconst __m0+ both setters + exactly oneJSON.parse), nosetRouteTrie/setPrecomputedEntriesemittedroute-types-hmr: 18 passedbuild-test-appbundle guard green; built entry inspected:setCachedManifest(__m0)+setRouterManifest("<id>", __m0)share one objectNotes
generateRoutesManifestModule.buildRouteToStaticPrefixre-export removal,genFileVarByEntryreplacing the dual-counter indexing. Skipped: none surviving.globalSearchSchemas/rootScopeRoutesare global, so same-named routes across routers last-writer-win) is deliberately not touched here — needs a repro first.