Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions scripts/size/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# @solidjs/router declares solid-js / @solidjs/web as peers; the scenarios
# alias both to the built dists, so the peers must not be installed here
# (they would pull a published solid-js into this lockfile for nothing).
legacy-peer-deps=true
17 changes: 15 additions & 2 deletions scripts/size/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ primitives — that keeps the store engine pay-for-use under `hydrate()`, three
compiled-template scenarios — a compiled floor and a JSX todo app in CSR and
hydrating form — the frames client as a package, two server-component PAGES:
base and live, their two compiled counterparts — the same pages as JSX with
the templates a real page has — and two server-entry floors:
`getRequestEvent`/`isServer` and
the templates a real page has — the same two hand-written pages under
`@solidjs/router`, and two server-entry floors: `getRequestEvent`/`isServer` and
`renderToString`) with hard brotli limits on the eager entry chunk. CI fails
when a scenario exceeds its limit and grew more than a small minified
allowance over its base (see [The gate](#the-gate)) — that means tree-shaking regressed, or a deliberate
Expand Down Expand Up @@ -123,6 +123,19 @@ switch re-based every cap (Rolldown lands 4–9% lower on the same artifacts;
the table is in the switch PR and each scenario's ledger note in
`scenarios.js`). Ledger deltas must not be read across that line.

## The router scenarios

`page: base + router` and `page: live + router` are the two page scenarios
plus `@solidjs/router` 2.0 — the Solid 2 line (`2.0.0-next.*`), a
devDependency of this directory pinned exactly like Rolldown (a router
upgrade is a re-base, recorded like any other cap change). It resolves from
this directory's `node_modules`; its `solid-js` / `@solidjs/web` imports go
through the page alias to the built dists like everything else, and
`.npmrc`'s `legacy-peer-deps` keeps npm from installing those peers from the
registry for nothing. The router's own modules report as the `other`
package. These are not floors: their caps are inline, and the two page
floors stay the gate on the runtime alone.

## Compiled scenarios

The hand-written `app:` fixtures call the runtime directly and never compile
Expand Down
18 changes: 18 additions & 0 deletions scripts/size/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scripts/size/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"build": "cd ../.. && pnpm install --frozen-lockfile && pnpm build"
},
"devDependencies": {
"@solidjs/router": "2.0.0-next.35",
"rolldown": "1.2.11"
}
}
49 changes: 49 additions & 0 deletions scripts/size/sc-live-router-app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// The live server-component page under a router: sc-live-app plus the same
// `@solidjs/router` 2.0 composition as sc-router-app (createRouter with two
// routes — one `preload`, one lazy — the instance as the hydrated root,
// `useNavigate` in a route component). See sc-router-app.js for why the
// router's runtime, not just its imports, is retained.
import {
hydrate,
Show,
For,
Loading,
Errored,
dynamicComponent,
createComponent
} from "@solidjs/web";
import { createSignal, createMemo, action, isPending, latest, lazy } from "solid-js";
import { installServerComponents } from "@solidjs/web/frames";
import { createServerReference, live, GET } from "@solidjs/web/server-functions/client";
import { createRouter, useNavigate } from "@solidjs/router";

installServerComponents();
const getStory = live(GET(createServerReference("story", "getStory")));
const Story = dynamicComponent(() => getStory());
const send = action(async () => {});
const [n, setN] = createSignal(0);
const Page = lazy(() => import("./lazy-page.js"));
function Home(props) {
const navigate = useNavigate();
return [props.children, () => navigate("/story/1")];
}
const Router = createRouter({
routes: [
{ path: "/", component: Home, preload: ({ params, intent }) => void (params, intent) },
{ path: "/story/:id", component: Page }
]
});
hydrate(() => {
const d = createMemo(() => n() + (isPending(n) ? 1 : 0) + latest(n));
setN(1);
return [
d(),
Show,
For,
Loading,
Errored,
Story,
send,
createComponent(Router, { children: p => p.children })
];
}, document.body);
56 changes: 56 additions & 0 deletions scripts/size/sc-router-app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// The base server-component page under a router: sc-base-app plus what an
// application page ships to route — `@solidjs/router` 2.0 (the Solid 2 line,
// `2.0.0-next.*`, installed by this directory's own package.json; its
// `solid-js` / `@solidjs/web` imports route to the dists through pageAlias
// like every other import here). `createRouter` with two routes — one with a
// `preload`, one whose component is the page's `lazy()` chunk — rendered as
// the hydrated root through the instance with a render-prop layout, and
// `useNavigate` read in a route component. 2.0 has no `<A>`: anchors are
// plain `<a>`, claimed by the router through `registerElementClaim` and
// intercepted by delegation, so the link runtime is the router's own
// (`setupLinkClaims`, `setupNativeEvents`) and is retained by the instance.
// The instance, its routes and the route components are all reachable from
// the root, so the router's runtime — matching, the integration, the
// context, preloading, link claims — is retained, not just its imports.
import {
hydrate,
Show,
For,
Loading,
Errored,
dynamicComponent,
createComponent
} from "@solidjs/web";
import { createSignal, createMemo, lazy } from "solid-js";
import { installServerComponents } from "@solidjs/web/frames";
import { createServerReference } from "@solidjs/web/server-functions/client";
import { createRouter, useNavigate } from "@solidjs/router";

installServerComponents();
const getStory = createServerReference("story", "getStory");
const Story = dynamicComponent(() => getStory());
const [n, setN] = createSignal(0);
const Page = lazy(() => import("./lazy-page.js"));
function Home(props) {
const navigate = useNavigate();
return [props.children, () => navigate("/story/1")];
}
const Router = createRouter({
routes: [
{ path: "/", component: Home, preload: ({ params, intent }) => void (params, intent) },
{ path: "/story/:id", component: Page }
]
});
hydrate(() => {
const d = createMemo(() => n() + 1);
setN(1);
return [
d(),
Show,
For,
Loading,
Errored,
Story,
createComponent(Router, { children: p => p.children })
];
}, document.body);
61 changes: 61 additions & 0 deletions scripts/size/scenarios.js
Original file line number Diff line number Diff line change
Expand Up @@ -4305,6 +4305,67 @@ module.exports = [
capMinified: 122779,
alias: pageAlias
},
{
name: "page: base + router (base page + @solidjs/router: createRouter, two routes, preload, useNavigate)",
// The base page as an application ships it: with a router. The two page
// floors above carry no router, so the "base case" they gate is a page
// nobody deploys; this scenario is the same composition plus
// `@solidjs/router` 2.0 (`2.0.0-next.35`, pinned exactly in this
// directory's package.json — the Solid 2 line; a router upgrade is a
// re-base recorded like a Rolldown upgrade) with `createRouter`, two
// routes (one `preload`, one lazy), the instance as the hydrated root
// and `useNavigate` in a route component. Not a floor: an inline cap,
// and the router's growth is the router's — this scenario attributes it
// (the `other` package in the report is the router).
// Landing (2026-10-06, next @ 49a8dca84): measured locally at 57,001 B
// (184,378 B minified) against the base page's 44,864 (145,599) — the
// router's contribution +12,137 B br / +38,779 B minified: the router's
// own 28,008 minified (`createRouterContext`, `setupNativeEvents` incl.
// the form-action path, `query`'s preload cache, `setupLinkClaims`,
// scroll restoration, `browserHistory`) plus what it retains of ours —
// signals +8,583 (`action` and the optimistic lanes: `verdictValue`,
// `dissolveLane`, `laneRead`, `isPending`/`latest`, `createEffect`,
// `onSettled`), sf +1,188 (`subscribeFlightData`, `decodeResponsePayload`,
// the redirect header, `parseServerFunctionAddress`), web +1,137
// (`takeHydrationValue`, `registerElementClaim`), solid +364
// (`createContext`, `children`). The router standalone, with solid-js /
// @solidjs/web external, is 32,430 / 11,216 B.
// Re-based onto `next` @ 7233451ee (#3838, 2026-10-07), after the frames
// tiers (#3860): measured at 45,940 B (144,082 B minified) against the
// base page's 33,908 (105,252) — the router's contribution +12,032 B br
// / +38,830 B minified, the same as at landing; the page under it is
// what #3860 cut. The fixture now mounts through `dynamicComponent`, the
// documented server-component mount the base page uses (#3870); with
// `dynamic` it measures 48,272 / 151,551 (+2,332 / +7,469: `dynamic`'s
// tag arm keeps the element runtime). The router does not retain that
// runtime itself — its `serverRouteComponent` helper mounts with
// `dynamic`, but this page does not use it. Cap at measured + 10 B
// rounded up to 0.01 KB; recorded minified 144,082 B (CI-confirmed).
path: "sc-router-app.js",
limit: "45.95 KB",
capMinified: 144082,
alias: pageAlias
},
{
name: "page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate)",
// The live page with the same router (see the base + router note).
// Landing (2026-10-06, next @ 49a8dca84): measured locally at 58,243 B
// (188,800 B minified) against the live page's 48,527 (157,562) — the
// router's contribution +9,716 B br / +31,238 B minified, less than on
// the base page because the live page already carries `action` and
// `isPending`/`latest`, which the router retains on every page.
// Re-based onto `next` @ 7233451ee (#3838, 2026-10-07), after the frames
// tiers (#3860): measured at 47,197 B (148,526 B minified) against the
// live page's 37,512 (117,301) — the router's contribution +9,685 B br /
// +31,225 B minified. Mounted through `dynamicComponent` as the live page
// is (the base + router note); with `dynamic`: 49,475 / 155,995. Cap at
// measured + 10 B rounded up to 0.01 KB; recorded minified 148,526 B
// (CI-confirmed).
path: "sc-live-router-app.js",
limit: "47.21 KB",
capMinified: 148526,
alias: pageAlias
},
{
name: "server: floor (getRequestEvent + isServer)",
// What a server module that only asks "am I on the server / which
Expand Down
Loading