fix: hint the core runtime in the head and measure pre-boot clicks - #1281
Draft
vivek7405 wants to merge 1 commit into
Draft
fix: hint the core runtime in the head and measure pre-boot clicks#1281vivek7405 wants to merge 1 commit into
vivek7405 wants to merge 1 commit into
Conversation
Links are clickable from first paint, but the boot is a module script, which the HTML spec defers until parsing finishes. A click inside that window is a plain browser navigation. The window cannot be closed from inside the router (an inline capture shim has no prior art, forks the eligibility rules in onClick, and adds an inline script to every page), so it is shrunk and then measured. Shrinking it: every module the boot imports pulls @webjsdev/core, but the boot script names only page and component URLs, so the browser discovered core one full round trip late. The head now hints it, with the href taken straight from the importmap target rather than through fp(), since the map's targets are already base-path-prefixed and content-hashed and a differing href would make the browser fetch core twice. Gated on the page actually shipping a boot module, so a fully elided page is not handed a preload for a runtime it never loads. vendorPreloadTargets still excludes core: the hint comes from the head builder, not the vendor path. Measuring what is left: a document that arrived by a same-origin navigation the router did not soft-navigate now reports pre-boot-navigation through the existing webjs:navigation-fallback channel, with willReload false because the load already happened. A marker in sessionStorage keeps a full load the router itself chose from being counted twice. A reload, a back/forward restore and an external entry are excluded. The check is deliberately imprecise (a data-no-router link and a cross-document form post land here too) and the docs say so: the signal is the rate, not any single event.
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.
Closes #1118
Links are clickable from first paint, but the boot is a
<script type="module">,which the HTML spec defers until parsing finishes. A click inside that window is
a plain browser navigation: a full document load on a site whose router is
supposed to soft-navigate. Measured on the live site, the router starts
listening at 25 to 35 ms warm and 313 ms cold on 300 kbps / 200 ms RTT.
The window cannot be closed from inside the router, so this shrinks it and
measures what is left. No inline capture shim (see the Rejected section on the
issue: no prior art in Turbo, zero reproductions in 56 measured clicks, and it
would fork the eligibility rules that live in
onClick).Shrinking it
Every module the boot imports pulls
@webjsdev/core, but the boot script namesonly page and component URLs, so the browser discovered core one full round trip
into the load.
wrapHeadnow emits one<link rel="modulepreload">for it,ahead of the page and component hints since all of them import it.
fp(). Themap's targets are already base-path-prefixed and content-hashed; a differing
href makes the browser treat the preload and the import as two resources and
fetch core twice, silently. This copies the vendor loop, not the module loop.
opts.moduleUrls.length || lazyEntries, so a fully elided page isnot handed a preload for a runtime it never loads (App-module modulepreload over-fetches a dropped page's SSR-only subtree #780). That also keeps it
off
global-error.{js,ts}, whose document is returned verbatim.vendorPreloadTargetsis unchanged and still excludes core. A test pins thatseam, since "fixing" the exclusion there would reintroduce the double-fetch.
Measuring what is left
A document that arrived by a same-origin navigation the router did not
soft-navigate reports
pre-boot-navigationthrough the existingwebjs:navigation-fallbackchannel (#1114),willReload: falsebecause theload already happened.
reportFallbackleaves asessionStoragemarker namingthe destination when it chooses a full load, and the next boot consumes it, so a
degradation the router already reported under its own cause is not counted
twice. A reload, a back/forward restore and an external or typed entry are
excluded. The predicate is extracted pure and exported for unit testing.
Deliberately imprecise, and the docs say so: a
data-no-routerlink, atarget="_blank"open and a cross-document form post land here too, and a fullyelided page reports nothing since the report rides the router's own boot. The
signal is the rate, not any single event.
Verification
WEBJS_E2E=1: the served page carries exactly one coremodulepreloadand the browser issues exactly ONE request for that URL acrossa cold load, which is what proves byte-identity rather than eyeballing the
tag. Second: a listener installed with
evaluateOnNewDocument(nothingin-page can register before the boot module) sees
pre-boot-navigationwithwillReload: falseon a same-originlocation.hrefload, and sees nothing onthe entry load, which is the control against a router that reports every load.
ssr.jsalone reds the preload assertion andleaves the event one green; reverting
router-client.jsalone reds the eventassertion and leaves the preload one green.
vendor-preload.test.js(one core hint, hrefequal to the importmap target, none on an elided page,
vendorPreloadTargetsstill excludes core); the predicate cases in
router-client.test.js(
navigate+ same-origin referrer true;reload,back_forward,cross-origin, empty and unparseable referrer, and a matching marker all
false; a stale marker for another href does not suppress).
/ssr\.js): new pairtest/bun/core-modulepreload.{mjs,test.mjs}, passing on Node and on Bun.and WebKit.
Docs
website/app/docs/client-router/page.tsand.agents/skills/webjs/references/client-router-and-streaming.md: the new causeadded to both enumerated lists, plus what it means, why the head hint exists,
and the stated imprecision.
Merge order: this lands SECOND, after #1252 (PR #1279). Both touch
router-client.jsin disjoint regions; #1252 is a correctness bug on a shippedfeature, this is a load-time and observability improvement.