Merge main into upgrade-solidjs-rc-0 - #8059
Conversation
ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(router): preserve raw params for match identity * refactor(router): return matched routes as tuple * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix(react-router): prevent blank initial pending frame * test(react-router): remove obsolete issue 4759 unit test * alternative fix * ci: apply automated fixes * simplify code comment --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* test disabled viewport links in Vue * test reactive intersection observer disabling * pass intersection observer disabled state directly * changeset
* test createFileRoute factory behavior * create file routes directly * preserve createFileRoute parameter name * cleanup * changeset
* perf(history): compact queued action kind * perf(history): compact queued navigation state * perf(history): derive queued action state * perf(router-core): tuple lightweight route matches * perf(solid-router): compact nearest match context * perf(vue-router): reuse the computed scripts tuple * perf(vue-router): flatten private href input * perf(vue-router): flatten private active-state input * perf(vue-router): flatten private style input * perf(start): return fetch bodies without an internal wrapper * test(history): cover browser action batching * docs: record compact private boundary results * Delete RESULT-optimization-compact-private-boundaries.md
ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(router): retain mounted UI during revalidation * fix(router): preserve initial pending presentation * changeset * test(router): make retained pending tests deterministic * complete solid/vue suite
ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…7978) * perf(start-client-core): compact frame decoder result keys * docs: record compact frame result evidence * docs: record frame result attribution and e2e * Delete RESULT-optimization-frame-decoder-compact-result.md * changeset
* perf(solid-start): shorten private prefetch state * perf(start): use locals for hydration wait state * perf(start): compact visible observer registry entries * test(start): cover hydration private-state lifecycles * docs: record solid hydration bundle result * Delete RESULT-optimization-solid-hydration-private-state.md * changeset * ci: Version Packages (#7993) [Self-Healing CI Rerun] --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
…state are unchanged (#7952) * perf(react-router): bail out of Link re-renders when href and active state are unchanged useLinkProps subscribes to the location store with an identity selector and an href comparator, then derives href and isActive from the published location in downstream memos. The comparator can only ask "is this a different URL?", never "does this link care?", so every Link on the page re-renders on every navigation. Move the location-derived values into the selector and compare them, so a link whose resolved href and active state are unaffected by a navigation bails out. buildLocation still runs once per link per location change; what goes away is the React render and the host reconciliation under it. doPreload no longer pre-supplies _builtLocation, because the built location is no longer kept in render state. preloadRoute already falls back to building it, which is what handleClick has always relied on for router.navigate. The isActive and externalLink bodies move to module-level helpers unchanged so the selector stays readable; activeOptions is spread into its four primitive fields in the dependency list because callers routinely pass an inline object literal. * refactor: trim redundant comments and brace single-line bodies The comments explained the bail-out rationale twice — once on the LinkState type and again above the selector — and two helper docblocks restated their function names. The rationale now appears once, where a reader meets the selector; the detail belongs in the PR description rather than the source. Also braces the three single-line if bodies, per the AGENTS.md rule that if/else bodies always use curly braces. * refactor: publish link state as a tuple The type is erased either way, but the object literal's property names survive minification and a tuple's positions don't — so this drops three property names from the selector's return plus the three property reads in compareLinkState. Measured on the unminified build: -54 bytes in dist/esm/link.js and the same in dist/cjs/link.cjs. * test: assert the published link state, and drop a redundant cast The render-count assertions proved the bail-out but not that the selector still publishes correct values, so a selector returning a constant could have passed. The test now also asserts that the link gaining active state carries `data-status="active"` afterwards (and does not beforehand), and that the unaffected link keeps its href and stays inactive. Checked by sabotaging the selector: returning a constant tuple with a wrong href but a correct active state now fails, where previously it passed. Also drops `as any` from `new URL(to)` in resolveExternalLink — the guard above already narrows `to` to string. * refactor: pass activeOptions through instead of destructuring Depends on the four fields rather than the object, with an exhaustive-deps disable: callers routinely pass an inline literal, which would otherwise rebuild the selector every render. resolveIsActive reads only those four fields, so the disable is not hiding a live dependency. -269 bytes on each of dist/esm/link.js and dist/cjs/link.cjs (unminified). * perf: memoize the href derivation on the built href The `useMemo` chain this replaced keyed `getHrefOption` and the external-link resolution on the href string, so a navigation that left a link's href alone skipped both. Deriving everything in the selector ran them on every location notification instead, which showed up as a ~10% regression on the client-nav rewrites benchmark, where rewrite handling makes `getHrefOption` expensive. Cache both on the built href inside the selector closure. Measured on a five-link root layout, per navigation: getHrefOption drops from 5 calls back to 0, matching the pre-change profile, with buildLocation and the active-state derivation unchanged at 5. * perf: keep _options referentially stable while its contents are equal Links commonly pass inline `params` / `search` object literals. Those change identity on every parent render, which rebuilt `_options`, which changed the store selector's identity, which discarded useSyncExternalStoreWithSelector's memoized selection. buildLocation then ran twice per navigation: once in the notification check and once in the render-phase selection. Measured on a replica of the client-nav rewrites scenario (six links, root subscribed to the pathname via useLocation), buildLocation per navigation: base 7, before this commit 12, after 7. * revert: drop the href memoization, it measured no benefit Reverts 409371b. It did cut getHrefOption from 5 calls per navigation to 0, matching the pre-change profile, but that is not where the time went: on the rewrites scenario it moved the number by 0.05% (medians 245.33 vs 245.21 hz over four interleaved rounds). Not worth ~15 lines of mutable closure state. The rewrites regression is fixed by the _options stabilisation instead. * docs: explain why useValueStable exists Replaces a leftover scratch note. * perf: cut the bundle cost of the link state selector Stabilise `activeOptions` with the same helper used for `search` / `params`, so the selector depends on one value instead of four destructured fields. That also makes the dependency array honest, so the exhaustive-deps disable goes. Drop the reference-equality guard in useValueStable: deepEqual already short-circuits on `a === b`, so the guard only saved a function call that returns immediately. Measured with benchmarks/bundle-size against this branch's base, gzip delta across the eight React scenarios moves from +27/-7 to +11/-24, and raw bytes go uniformly negative (-37 to -45 on every scenario). * perf: pass _options to preloadRoute without the shallow clone The spread existed to add _builtLocation, which is gone, and nothing on the preload path mutates the options object: preloadRoute only reads opts._builtLocation, build() only reads dest fields, and the search middleware chain only reads dest.search. Search middlewares themselves receive { search, next }, never dest. Saves an object allocation per hover. Bytes are unchanged on gzip, -5 raw. * fix: do not collapse explicit-undefined params when stabilising link options useValueStable compared with deepEqual's default ignoreUndefined: true, which skips undefined-valued keys on both sides, so `{}` and `{ category: undefined }` compared equal. Those build different locations: an explicit undefined clears an inherited optional param while an empty object inherits it. A Link whose params changed from one to the other kept publishing the stale href, and click and preload used the stale options too. Compare with ignoreUndefined: false. Adds a regression test covering the /posts/tech -> /posts transition, which fails before this commit and passes after, and passes on the pre-PR baseline. * chore: empty commit to re-trigger CI * chore: add changeset --------- Co-authored-by: Flo <me@florianpellet.com>
ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ring (#8010) * perf(router-core): faster enumerable-key collection in structural sharing getEnumerableOwnKeys (used by replaceEqualDeep) called Object.getOwnPropertyNames and then invoked propertyIsEnumerable once per key to verify every own string prop is enumerable. Replace that O(n) loop of JS method calls with Object.keys (native, enumerable-only) plus a single length comparison against getOwnPropertyNames to detect non-enumerable string props. Symbol handling is unchanged. Behavior is identical (verified against the existing suite + fuzzing): the function still returns the enumerable own keys in the same order and still bails (returns false) for objects with any non-enumerable own string or symbol property. replaceEqualDeep runs on every selector result on every state update when defaultStructuralSharing is enabled, so this is a hot client path for selector-heavy apps. Measured ~1.3-1.5x faster on typical router state. * style(router-core): address enumerable key review --------- Co-authored-by: Yagiz Nizipli <yagiz@nizipli.com>
…8009) The frame decoder dropped consumed chunks from its buffer with bufferList.shift(), which is O(n). When a single large frame (e.g. a big RawStream payload) is assembled from many small network reads, the extract loop calls shift() once per chunk, making reassembly O(n^2). Track the first un-consumed chunk with a head pointer and advance it in O(1) instead of shifting. Consumed slots are released for GC, and the buffer is compacted when fully drained (O(1) reset) or once the consumed prefix grows past a small threshold (amortized O(1) per chunk). A micro-benchmark draining 1000 small chunks is ~11x faster. Co-authored-by: Yagiz Nizipli <yagiz@nizipli.com>
* perf(router-core): skip impossible JSON parses * perf(router-core): refine JSON parse fast path * changeset
ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(router-core): handle excessive parent relative links * changeset
ci: changeset release Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
View your CI Pipeline Execution ↗ for commit 6cb3c50
☁️ Nx Cloud last updated this comment at |
🚀 Changeset Version PreviewNo changeset entries found. Merging this PR will not cause a version bump for any packages. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
The #8002 fix itself is merged; its changeset stays on main for the 1.x release and would otherwise trigger a stray vue-router/solid-router bump in the rc pre-release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…/router into upgrade-solidjs-rc-0-main-merge
Hooray! CodSpeed harness just leveled up!The base and head of this comparison were measured with different runner settings, so their benchmark values are not directly comparable. What changed between base and head:
Re-run the base with the same settings to get a valid performance comparison. Comparing |
The merge kept the rc branch's lockfile, which had drifted: @babel/traverse resolved @babel/types 7.29.8 while router-plugin's direct dep stayed 7.28.5, so main's new code-splitter code failed to typecheck in CI. Re-resolving from main's lockfile (green with that code) plus the merged manifests restores a consistent babel graph. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bundle Size Benchmarks
The following scenarios have bundle-size changes compared with the baseline:
Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better. |
Merges
maininto the Solid 2 rc branch and resolves ~90 conflicts.Resolution summary
package.json (~75 files): kept the rc versions for the Solid family (
solid-js,@solidjs/*,@tanstack/solid-*,@rsbuild/plugin-solid, catalog entries) and took main's newer versions for everything else (@tanstack/router-plugin^1.168.30,@tanstack/history^1.162.1,@tanstack/react-router^1.170.27, etc.), unioning keys added by only one side.CHANGELOGs: kept the
2.0.0-*entries, matching how previous main merges on this line were resolved.Benchmarks (13
main.tsxfiles): kept Solid 2 imports (renderfrom@solidjs/web) and adopted main'sRouterHistoryimport from@tanstack/history.Source files — ported main's changes onto the Solid 2 rewrites:
Match.tsx/CatchBoundary.tsx: dev-moderenderInNonRouteComponentContextwrappers and the<Outlet />-in-fallback warning (fix(router): warn when Outlet is rendered in fallback components #8045);nearestMatchContexttuple shape (perf: compact private bundle boundaries #7975); default route component remounting fix (Fix default route component remounting #8002)nonRouteComponentContext.tsx: adapted for Solid 2 — default-less contexts throw onuseContextwithout a provider, so it defaults tonull, and the context object itself is the provider componentlink.tsx: unifiedenqueuePreloadwith preload delay applied to viewport links (fix(router): apply preload delay to viewport links #8044) and removal of Link transition state (refactor: remove Link transition state #8043)utils.ts: newuseIntersectionObserver(ref, callback, disabled)signature (Pass intersection observer disabled state directly #7971) in Solid 2 effect styleuseBlocker.tsx: tuple return ofgetMatchedRoutesfor raw params (fix(router): preserve raw params for match identity #7966)useRouterState.tsx: kept the rc branch's removal of the SSR early-return (main's inline isServer for proper DCE #7996 only touched that removed block)GenericHydrate.tsx: applied main's private-state field compaction renames (perf(start): compact deferred hydration private state #7980)@tanstack/router-coreand@tanstack/solid-router+@tanstack/solid-start-clientpasstsctypechecks; lockfile verified consistent viapnpm install --lockfile-only.🤖 Generated with Claude Code