@rsc-kit/core 0.20.6 → 0.20.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,22 @@
1
1
  /**
2
- * Prefetching a link as it comes into view, where there is no pointer to hover it.
2
+ * Prefetching a link as it comes into view.
3
3
  *
4
- * On a phone the first signal a tap gives is touchstart, and the click lands
5
- * 100-300 ms after it - about a round trip - so a prefetch started there has
6
- * barely left when the navigation needs it. Next prefetches the links on
7
- * screen instead, and that is what makes its taps feel instant on a phone.
8
- * Only on a device with no hover: a pointer that can settle on a link is a
9
- * better signal than a link merely being visible, and cheaper on a page with
10
- * a hundred of them.
4
+ * A navigation is as fast as what is already in the browser when the click
5
+ * lands, and a hover is too late to fetch that: a pointer that has settled
6
+ * on a link is 200-300 ms from clicking it, and on a phone the first signal
7
+ * is touchstart, 100-300 ms before the click - about a round trip either
8
+ * way, so a fetch started there has barely left when the navigation needs
9
+ * it. Next prefetches the links on screen instead, and that is what makes
10
+ * its clicks feel like a single-page app's: the payload was there before
11
+ * the pointer moved. So, on every device, the links on screen are fetched -
12
+ * the bytes only; decoding a payload loads the chunks it names, and that
13
+ * waits for the hover or the touch that says which link is meant. It used
14
+ * to be phones only, with a hover-capable device left to the hover: a click
15
+ * that came quicker than the round trip waited for it, and desktop was
16
+ * fast but not instant.
17
+ *
18
+ * Not when the visitor asked for less data: Save-Data is the one signal a
19
+ * browser gives that speculative requests are unwelcome.
11
20
  *
12
21
  * One observer for every link, and the work is done when the browser is
13
22
  * idle - a list scrolled into view is many links at once, and the browser's
@@ -16,8 +25,9 @@
16
25
  */
17
26
  let observer = null;
18
27
  const pending = new WeakMap();
19
- function noHover() {
20
- return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(hover: none)").matches;
28
+ function savingData() {
29
+ const connection = navigator.connection;
30
+ return connection?.saveData === true;
21
31
  }
22
32
  function whenIdle(fn) {
23
33
  const idle = window.requestIdleCallback;
@@ -48,11 +58,11 @@ function observerFor() {
48
58
  return observer;
49
59
  }
50
60
  /**
51
- * Prefetch when the link is on screen, on a device with no hover. Returns
52
- * the function that stops watching; a no-op where this does not apply.
61
+ * Prefetch when the link is on screen. Returns the function that stops
62
+ * watching; a no-op where this does not apply.
53
63
  */
54
64
  export function prefetchWhenVisible(element, fire) {
55
- if (!element || !noHover())
65
+ if (!element || savingData())
56
66
  return () => { };
57
67
  const io = observerFor();
58
68
  if (!io)
@@ -1 +1 @@
1
- {"version":3,"file":"viewportPrefetch.js","sourceRoot":"","sources":["../../src/js/viewportPrefetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,IAAI,QAAQ,GAAgC,IAAI,CAAA;AAChD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAuB,CAAA;AAElD,SAAS,OAAO;IACd,OAAO,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,OAAO,CAAA;AAC/H,CAAC;AAED,SAAS,QAAQ,CAAC,EAAc;IAC9B,MAAM,IAAI,GAAI,MAAyF,CAAC,mBAAmB,CAAA;IAE3H,IAAI,IAAI;QAAE,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;;QAChC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;AACzB,CAAC;AAED,SAAS,WAAW;IAClB,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,IAAI,OAAO,oBAAoB,KAAK,WAAW;QAAE,OAAO,IAAI,CAAA;IAE5D,QAAQ,GAAG,IAAI,oBAAoB,CACjC,CAAC,OAAO,EAAE,EAAE;QACV,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,cAAc;gBAAE,SAAQ;YAEnC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YAEtC,QAAS,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YAE5B,IAAI,IAAI;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC;IACD,sEAAsE;IACtE,sBAAsB;IACtB,EAAE,UAAU,EAAE,OAAO,EAAE,CACxB,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAuB,EAAE,IAAgB;IAC3E,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IAE3C,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IAExB,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IAExB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC1B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAEnB,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACvB,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACvB,CAAC,CAAA;AACH,CAAC","sourcesContent":["/**\n * Prefetching a link as it comes into view, where there is no pointer to hover it.\n *\n * On a phone the first signal a tap gives is touchstart, and the click lands\n * 100-300 ms after it - about a round trip - so a prefetch started there has\n * barely left when the navigation needs it. Next prefetches the links on\n * screen instead, and that is what makes its taps feel instant on a phone.\n * Only on a device with no hover: a pointer that can settle on a link is a\n * better signal than a link merely being visible, and cheaper on a page with\n * a hundred of them.\n *\n * One observer for every link, and the work is done when the browser is\n * idle - a list scrolled into view is many links at once, and the browser's\n * per-origin connections should not all be taken by pages nobody has tapped\n * yet. Each link prefetches once; the router keeps the payload for its TTL.\n */\n\nlet observer: IntersectionObserver | null = null\nconst pending = new WeakMap<Element, () => void>()\n\nfunction noHover(): boolean {\n return typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" && window.matchMedia(\"(hover: none)\").matches\n}\n\nfunction whenIdle(fn: () => void): void {\n const idle = (window as { requestIdleCallback?: (fn: () => void, opts?: { timeout: number }) => void }).requestIdleCallback\n\n if (idle) idle(fn, { timeout: 1000 })\n else setTimeout(fn, 50)\n}\n\nfunction observerFor(): IntersectionObserver | null {\n if (observer) return observer\n if (typeof IntersectionObserver === \"undefined\") return null\n\n observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (!entry.isIntersecting) continue\n\n const fire = pending.get(entry.target)\n\n observer!.unobserve(entry.target)\n pending.delete(entry.target)\n\n if (fire) whenIdle(fire)\n }\n },\n // A little ahead of the fold: a link about to scroll into view is one\n // about to be tapped.\n { rootMargin: \"200px\" },\n )\n\n return observer\n}\n\n/**\n * Prefetch when the link is on screen, on a device with no hover. Returns\n * the function that stops watching; a no-op where this does not apply.\n */\nexport function prefetchWhenVisible(element: Element | null, fire: () => void): () => void {\n if (!element || !noHover()) return () => {}\n\n const io = observerFor()\n\n if (!io) return () => {}\n\n pending.set(element, fire)\n io.observe(element)\n\n return () => {\n pending.delete(element)\n io.unobserve(element)\n }\n}\n"]}
1
+ {"version":3,"file":"viewportPrefetch.js","sourceRoot":"","sources":["../../src/js/viewportPrefetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,IAAI,QAAQ,GAAgC,IAAI,CAAA;AAChD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAuB,CAAA;AAElD,SAAS,UAAU;IACjB,MAAM,UAAU,GAAI,SAAqD,CAAC,UAAU,CAAA;IAEpF,OAAO,UAAU,EAAE,QAAQ,KAAK,IAAI,CAAA;AACtC,CAAC;AAED,SAAS,QAAQ,CAAC,EAAc;IAC9B,MAAM,IAAI,GAAI,MAAyF,CAAC,mBAAmB,CAAA;IAE3H,IAAI,IAAI;QAAE,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;;QAChC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;AACzB,CAAC;AAED,SAAS,WAAW;IAClB,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,IAAI,OAAO,oBAAoB,KAAK,WAAW;QAAE,OAAO,IAAI,CAAA;IAE5D,QAAQ,GAAG,IAAI,oBAAoB,CACjC,CAAC,OAAO,EAAE,EAAE;QACV,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,cAAc;gBAAE,SAAQ;YAEnC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YAEtC,QAAS,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YAE5B,IAAI,IAAI;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC1B,CAAC;IACH,CAAC;IACD,sEAAsE;IACtE,sBAAsB;IACtB,EAAE,UAAU,EAAE,OAAO,EAAE,CACxB,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAuB,EAAE,IAAgB;IAC3E,IAAI,CAAC,OAAO,IAAI,UAAU,EAAE;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IAE7C,MAAM,EAAE,GAAG,WAAW,EAAE,CAAA;IAExB,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IAExB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC1B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAEnB,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACvB,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACvB,CAAC,CAAA;AACH,CAAC","sourcesContent":["/**\n * Prefetching a link as it comes into view.\n *\n * A navigation is as fast as what is already in the browser when the click\n * lands, and a hover is too late to fetch that: a pointer that has settled\n * on a link is 200-300 ms from clicking it, and on a phone the first signal\n * is touchstart, 100-300 ms before the click - about a round trip either\n * way, so a fetch started there has barely left when the navigation needs\n * it. Next prefetches the links on screen instead, and that is what makes\n * its clicks feel like a single-page app's: the payload was there before\n * the pointer moved. So, on every device, the links on screen are fetched -\n * the bytes only; decoding a payload loads the chunks it names, and that\n * waits for the hover or the touch that says which link is meant. It used\n * to be phones only, with a hover-capable device left to the hover: a click\n * that came quicker than the round trip waited for it, and desktop was\n * fast but not instant.\n *\n * Not when the visitor asked for less data: Save-Data is the one signal a\n * browser gives that speculative requests are unwelcome.\n *\n * One observer for every link, and the work is done when the browser is\n * idle - a list scrolled into view is many links at once, and the browser's\n * per-origin connections should not all be taken by pages nobody has tapped\n * yet. Each link prefetches once; the router keeps the payload for its TTL.\n */\n\nlet observer: IntersectionObserver | null = null\nconst pending = new WeakMap<Element, () => void>()\n\nfunction savingData(): boolean {\n const connection = (navigator as { connection?: { saveData?: boolean } }).connection\n\n return connection?.saveData === true\n}\n\nfunction whenIdle(fn: () => void): void {\n const idle = (window as { requestIdleCallback?: (fn: () => void, opts?: { timeout: number }) => void }).requestIdleCallback\n\n if (idle) idle(fn, { timeout: 1000 })\n else setTimeout(fn, 50)\n}\n\nfunction observerFor(): IntersectionObserver | null {\n if (observer) return observer\n if (typeof IntersectionObserver === \"undefined\") return null\n\n observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (!entry.isIntersecting) continue\n\n const fire = pending.get(entry.target)\n\n observer!.unobserve(entry.target)\n pending.delete(entry.target)\n\n if (fire) whenIdle(fire)\n }\n },\n // A little ahead of the fold: a link about to scroll into view is one\n // about to be tapped.\n { rootMargin: \"200px\" },\n )\n\n return observer\n}\n\n/**\n * Prefetch when the link is on screen. Returns the function that stops\n * watching; a no-op where this does not apply.\n */\nexport function prefetchWhenVisible(element: Element | null, fire: () => void): () => void {\n if (!element || savingData()) return () => {}\n\n const io = observerFor()\n\n if (!io) return () => {}\n\n pending.set(element, fire)\n io.observe(element)\n\n return () => {\n pending.delete(element)\n io.unobserve(element)\n }\n}\n"]}
package/dist/vite.js CHANGED
@@ -5816,6 +5816,17 @@ export function rscKit(options = {}) {
5816
5816
  rollupOptions: {
5817
5817
  input: { index: join(genDir, "entry.rsc.tsx") },
5818
5818
  external: serverExternals,
5819
+ // The server-references map is `id: () => import(file)` for
5820
+ // every "use server" file, and the page that passes an action
5821
+ // to <Form> imports the same file statically. Rolldown says
5822
+ // so for each one: "dynamically imported by
5823
+ // virtual:vite-rsc/server-references but also statically
5824
+ // imported by page.tsx, dynamic import will not move module
5825
+ // into another chunk". Nothing was meant to move: a server
5826
+ // bundle has no chunk to lazy-load from a browser, and the
5827
+ // map is a lookup table, not a split point. One line per
5828
+ // action in every build was a paragraph nobody could act on.
5829
+ checks: { ineffectiveDynamicImport: false },
5819
5830
  },
5820
5831
  },
5821
5832
  resolve: { noExternal: bundledClientPackages },
@@ -5826,6 +5837,8 @@ export function rscKit(options = {}) {
5826
5837
  rollupOptions: {
5827
5838
  input: { index: join(genDir, "entry.ssr.tsx") },
5828
5839
  external: serverExternals,
5840
+ // Same table, same reason.
5841
+ checks: { ineffectiveDynamicImport: false },
5829
5842
  },
5830
5843
  },
5831
5844
  resolve: { noExternal: bundledClientPackages },