Repository navigation
perf(size): golf client entries before next.31 - #632
Merged
Merged
Conversation
DEV-only memo names, a one-expression segment matcher and filter lookup, one settle helper in query(), keysFor() on match()'s pathname derivation, and folded one-use closures / repeated property chains. Behavior is unchanged: client, server and type suites are identical to baseline. Co-authored-by: Claude <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 3fe292d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
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.
Code-size pass on the client entries ahead of
2.0.0-next.31. Every change is behaviour-preserving: the client (jsdom), server and type suites are identical before and after, dev-mode messages are untouched, and.shake-check/check.mjsstill passes. Measured againstnext@ 9600479 (the rebase base), so the numbers reflect what ships.Sizes
Tooling:
tsc && rollup -coutput, then terser + brotli (level 11) per entry — the same solid-condition bundling /isServer=falseshim /DEVfold the repo's.shake-checkscripts use. "Typical app" is a minimal Vite production build (createRouterwith three routes incl. one lazy,paths()links,useNavigate,useParams,query,action,createMemo,render), with solid bundled; "router share" is the same app withsolid-js/@solidjs/webexternalised so only the router's contribution is counted..(solid condition,dist/index.jsx).(default,dist/index.js).client-folded (isServer=false,DEVfolded)./fs./serverscripts/check-fs-gate.mjs:104.5 / 125.5 KB→104.1 / 125.0 KB.Brotli on inputs this small has roughly ±15 B of noise from context reshuffling, so individual steps are reported with that caveat; the cumulative numbers above are the ones that matter.
Changes (router share of the typical app, min / brotli)
keysFor()reusespathnameOf(url)(routers/factory.tsx) — it duplicated the pathname derivationmatch()already does. −44 / −11?.for route preload, one-expression segment encoder, flattenedpathsproxyget(routing.ts,paths.ts) —component?.preload?.()replaces the nestedifs;s => /^[:*]/.test(s) ? s : encodeSegment(s); the symbol branches inpathscollapse to tworeturns. −80 / −8window.history/history.statealiases (routers/history.ts) —saveCurrentDepthandbrowserHistory.getSourceread the chain once. Brotli-neutral (+2) but −118 min and easier to read; kept for that reason. (Merged with fix: don't throw when history.state is null after replaceState #627'sstate?._depth ?? length - 1.)nameoptions behindDEV(routing.ts) —DEV && { name: "lazyRoutes" }etc. Production solid never readsnameon memos/signals (onlycreateContextdoes), so these strings were pure dead weight in prod. −41 / −26matchSegment/ filter lookup (utils.ts) — one ternary chain instead of early-returnifs;filters?.[key]instead ofmatchFilters && matchFilters[key]. −60 / −21settle()helper inquery()(data/query.ts) — the cached and fresh paths each spelled out the samethen/non-thenable settle; now one local function. Brotli-neutral (+4), −21 min; kept because it removes a duplicated branch that has to stay in sync.routing.ts,data/action.ts,routers/components.tsx,routers/scrollRestoration.ts) — dropped the constantkeygetter closure increateLocation,disposers[i]?.(),formalias inhandleFormAction,response?.data/response?.error, destructuredperformance.getEntriesByType?.("navigation"). −68 / −13routing.ts,data/action.ts,data/events.ts) —new URL(mockBase)once instead of a separateoriginconst,fn.withreturnstoAction(...)directly,flightDatakeys loop tightened, redundantrel &&beforerel.includes(...). −11 / −13Tried and reverted (brotli-negative)
Brotli already compresses literal duplication very well, so extracting shared helpers to dedupe identical code consistently cost bytes on the wire even when it saved minified bytes: a shared
isExternalAnchor/isSvg/preloadAnchorforevents.ts+claims.ts(−370 min, +18 br), atoggleattribute helper inclaims.ts(+8 br), a sharedcommitEntryforbrowserHistory/hashHistory(+ on the browser-only typical app),Object.fromEntriesinhashKey, anisAsyncIterablehelper inliveQuery, spread-gated...(DEV && { name: "location" }). None are in this PR.Verification
Run on
next@ 9600479 and on this branch — identical:test:types: pass.shake-check/check.mjs: all 8 checks ok (outputs reverted, not committed)pnpm buildincl. fs gate: passPublic API changes
None.
Suggestions (not made — need an API decision)
scrollRestorationis always bundled.routers/scrollRestoration.tsships in every client bundle even for apps that passscrollRestoration: false. Making it an opt-in adapter (like the history adapters) would save every such app its full cost, but changes thecreateRouteroption shape.import("./serverForms.js")in the delegation fallback pulls Vite's__vitePreloadhelper (~1.1 KB min) into every router app, even ones that never render a server form. A static import or a documented opt-in would remove it; either is a behaviour/API decision, not golf.setupNativeEvents' curried signature and itspreloadoption name (vspreloadLinkson the router config) force an intermediate options object increateRouter(~90 B min). Internal, but tests import it directly, so left alone here.Deviations from the brief
Router,Route,A,createAsync. Those exports don't exist onnext(v2), so the app uses their v2 equivalents:createRouterwith a route array,paths()links on plain<a>,useNavigate,query,action, andcreateMemofor reads.solid-js), so it moves less in relative terms than the per-entry numbers.— Claude via Cursor