rwsdk 0.2.0-alpha.13 → 0.2.0-alpha.15

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.
Files changed (36) hide show
  1. package/dist/runtime/client/client.d.ts +10 -0
  2. package/dist/runtime/{client.js → client/client.js} +13 -10
  3. package/dist/runtime/{clientNavigation.test.js → client/navigation.test.js} +1 -1
  4. package/dist/runtime/client/setWebpackRequire.d.ts +1 -0
  5. package/dist/runtime/client/setWebpackRequire.js +2 -0
  6. package/dist/runtime/{client.d.ts → client/types.d.ts} +4 -10
  7. package/dist/runtime/client/types.js +1 -0
  8. package/dist/runtime/entries/client.d.ts +2 -2
  9. package/dist/runtime/entries/client.js +2 -2
  10. package/dist/runtime/imports/client.d.ts +3 -3
  11. package/dist/runtime/imports/client.js +6 -5
  12. package/dist/runtime/imports/ssr.d.ts +3 -3
  13. package/dist/runtime/imports/ssr.js +3 -3
  14. package/dist/runtime/imports/worker.d.ts +3 -3
  15. package/dist/runtime/imports/worker.js +3 -3
  16. package/dist/runtime/lib/manifest.d.ts +11 -2
  17. package/dist/runtime/lib/manifest.js +1 -1
  18. package/dist/runtime/lib/memoizeOnId.d.ts +1 -0
  19. package/dist/runtime/lib/memoizeOnId.js +11 -0
  20. package/dist/runtime/lib/realtime/client.d.ts +1 -1
  21. package/dist/runtime/lib/realtime/client.js +1 -1
  22. package/dist/runtime/register/ssr.d.ts +1 -1
  23. package/dist/runtime/register/ssr.js +2 -2
  24. package/dist/runtime/render/preloads.d.ts +6 -0
  25. package/dist/runtime/render/preloads.js +40 -0
  26. package/dist/runtime/render/renderRscThenableToHtmlStream.js +2 -1
  27. package/dist/runtime/render/stylesheets.js +1 -1
  28. package/dist/runtime/requestInfo/types.d.ts +3 -1
  29. package/dist/runtime/worker.js +1 -1
  30. package/dist/vite/miniflareHMRPlugin.mjs +3 -0
  31. package/package.json +1 -1
  32. /package/dist/runtime/{imports → client}/ClientOnly.d.ts +0 -0
  33. /package/dist/runtime/{imports → client}/ClientOnly.js +0 -0
  34. /package/dist/runtime/{clientNavigation.d.ts → client/navigation.d.ts} +0 -0
  35. /package/dist/runtime/{clientNavigation.js → client/navigation.js} +0 -0
  36. /package/dist/runtime/{clientNavigation.test.d.ts → client/navigation.test.d.ts} +0 -0
@@ -0,0 +1,10 @@
1
+ import "./setWebpackRequire";
2
+ export { ClientOnly } from "./ClientOnly.js";
3
+ export { default as React } from "react";
4
+ import type { Transport, HydrationOptions } from "./types";
5
+ export declare const fetchTransport: Transport;
6
+ export declare const initClient: ({ transport, hydrateRootOptions, handleResponse, }?: {
7
+ transport?: Transport;
8
+ hydrateRootOptions?: HydrationOptions;
9
+ handleResponse?: (response: Response) => boolean;
10
+ }) => Promise<void>;
@@ -1,11 +1,18 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
- import { clientWebpackRequire } from "./imports/client";
3
- // NOTE: `react-server-dom-webpack` uses this global to load modules,
4
- // so we need to define it here before importing "react-server-dom-webpack."
5
- globalThis.__webpack_require__ = clientWebpackRequire;
2
+ // note(justinvdm, 14 Aug 2025): Rendering related imports and logic go here.
3
+ // See client.tsx for the actual client entrypoint.
4
+ // context(justinvdm, 14 Aug 2025): `react-server-dom-webpack` uses this global
5
+ // to load modules, so we need to define it here before importing
6
+ // "react-server-dom-webpack."
7
+ import "./setWebpackRequire";
8
+ import React from "react";
9
+ import { hydrateRoot } from "react-dom/client";
10
+ import { createFromReadableStream, createFromFetch, encodeReply, } from "react-server-dom-webpack/client.browser";
11
+ import { rscStream } from "rsc-html-stream/client";
12
+ export { ClientOnly } from "./ClientOnly.js";
13
+ export { default as React } from "react";
6
14
  export const fetchTransport = (transportContext) => {
7
15
  const fetchCallServer = async (id, args) => {
8
- const { createFromFetch, encodeReply } = await import("react-server-dom-webpack/client.browser");
9
16
  const url = new URL(window.location.href);
10
17
  url.searchParams.set("__rsc", "");
11
18
  if (id != null) {
@@ -41,8 +48,6 @@ export const fetchTransport = (transportContext) => {
41
48
  return fetchCallServer;
42
49
  };
43
50
  export const initClient = async ({ transport = fetchTransport, hydrateRootOptions, handleResponse, } = {}) => {
44
- const React = await import("react");
45
- const { hydrateRoot } = await import("react-dom/client");
46
51
  const transportContext = {
47
52
  setRscPayload: () => { },
48
53
  handleResponse,
@@ -50,7 +55,7 @@ export const initClient = async ({ transport = fetchTransport, hydrateRootOption
50
55
  let transportCallServer = transport(transportContext);
51
56
  const callServer = (id, args) => transportCallServer(id, args);
52
57
  const upgradeToRealtime = async ({ key } = {}) => {
53
- const { realtimeTransport } = await import("./lib/realtime/client");
58
+ const { realtimeTransport } = await import("../lib/realtime/client");
54
59
  const createRealtimeTransport = realtimeTransport({ key });
55
60
  transportCallServer = createRealtimeTransport(transportContext);
56
61
  };
@@ -67,8 +72,6 @@ export const initClient = async ({ transport = fetchTransport, hydrateRootOption
67
72
  // context(justinvdm, 18 Jun 2025): We inject the RSC payload
68
73
  // unless render(Document, [...], { rscPayload: false }) was used.
69
74
  if (globalThis.__FLIGHT_DATA) {
70
- const { createFromReadableStream } = await import("react-server-dom-webpack/client.browser");
71
- const { rscStream } = await import("rsc-html-stream/client");
72
75
  rscPayload = createFromReadableStream(rscStream, {
73
76
  callServer,
74
77
  });
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { validateClickEvent } from "./clientNavigation";
2
+ import { validateClickEvent } from "./navigation";
3
3
  describe("clientNavigation", () => {
4
4
  let mockEvent = {
5
5
  button: 0, // right click
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { clientWebpackRequire } from "../imports/client";
2
+ globalThis.__webpack_require__ = clientWebpackRequire;
@@ -1,19 +1,13 @@
1
- import { type CallServerCallback } from "react-server-dom-webpack/client.browser";
2
- import { type HydrationOptions } from "react-dom/client";
1
+ import type { CallServerCallback } from "react-server-dom-webpack/client.browser";
2
+ export type { CallServerCallback } from "react-server-dom-webpack/client.browser";
3
+ export type { HydrationOptions } from "react-dom/client";
3
4
  export type ActionResponse<Result> = {
4
5
  node: React.ReactNode;
5
6
  actionResult: Result;
6
7
  };
7
- type TransportContext = {
8
+ export type TransportContext = {
8
9
  setRscPayload: <Result>(v: Promise<ActionResponse<Result>>) => void;
9
10
  handleResponse?: (response: Response) => boolean;
10
11
  };
11
12
  export type Transport = (context: TransportContext) => CallServerCallback;
12
13
  export type CreateCallServer = (context: TransportContext) => <Result>(id: null | string, args: null | unknown[]) => Promise<Result>;
13
- export declare const fetchTransport: Transport;
14
- export declare const initClient: ({ transport, hydrateRootOptions, handleResponse, }?: {
15
- transport?: Transport;
16
- hydrateRootOptions?: HydrationOptions;
17
- handleResponse?: (response: Response) => boolean;
18
- }) => Promise<void>;
19
- export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  import "./types/client";
2
- export * from "../client";
2
+ export * from "../client/client";
3
3
  export * from "../register/client";
4
4
  export * from "../lib/streams/consumeEventStream";
5
- export * from "../clientNavigation";
5
+ export * from "../client/navigation";
@@ -1,5 +1,5 @@
1
1
  import "./types/client";
2
- export * from "../client";
2
+ export * from "../client/client";
3
3
  export * from "../register/client";
4
4
  export * from "../lib/streams/consumeEventStream";
5
- export * from "../clientNavigation";
5
+ export * from "../client/navigation";
@@ -1,4 +1,4 @@
1
- export declare const loadModule: ((id: string) => Promise<any>) & import("lodash").MemoizedFunction;
2
- export declare const clientWebpackRequire: ((id: string) => Promise<{
1
+ export declare const loadModule: (id: string) => Promise<any>;
2
+ export declare const clientWebpackRequire: (id: string) => Promise<{
3
3
  [x: string]: any;
4
- }>) & import("lodash").MemoizedFunction;
4
+ }>;
@@ -1,11 +1,13 @@
1
1
  import React from "react";
2
- import memoize from "lodash/memoize";
3
- export const loadModule = memoize(async (id) => {
2
+ import { ClientOnly } from "../client/client";
3
+ import { memoizeOnId } from "../lib/memoizeOnId";
4
+ // @ts-ignore
5
+ import { useClientLookup } from "virtual:use-client-lookup.js";
6
+ export const loadModule = memoizeOnId(async (id) => {
4
7
  if (import.meta.env.VITE_IS_DEV_SERVER) {
5
8
  return await import(/* @vite-ignore */ id);
6
9
  }
7
10
  else {
8
- const { useClientLookup } = await import("virtual:use-client-lookup.js");
9
11
  const moduleFn = useClientLookup[id];
10
12
  if (!moduleFn) {
11
13
  throw new Error(`(client) No module found for '${id}' in module lookup for "use client" directive`);
@@ -14,7 +16,7 @@ export const loadModule = memoize(async (id) => {
14
16
  }
15
17
  });
16
18
  // context(justinvdm, 2 Dec 2024): re memoize(): React relies on the same promise instance being returned for the same id
17
- export const clientWebpackRequire = memoize(async (id) => {
19
+ export const clientWebpackRequire = memoizeOnId(async (id) => {
18
20
  const [file, name] = id.split("#");
19
21
  const promisedModule = loadModule(file);
20
22
  const promisedComponent = promisedModule.then((module) => module[name]);
@@ -23,7 +25,6 @@ export const clientWebpackRequire = memoize(async (id) => {
23
25
  const awaitedComponent = await promisedComponent;
24
26
  return { [id]: awaitedComponent };
25
27
  }
26
- const { ClientOnly } = await import("./ClientOnly");
27
28
  const promisedDefault = promisedComponent.then((Component) => ({
28
29
  default: Component,
29
30
  }));
@@ -1,5 +1,5 @@
1
- export declare const ssrLoadModule: ((id: string) => Promise<any>) & import("lodash").MemoizedFunction;
1
+ export declare const ssrLoadModule: (id: string) => Promise<any>;
2
2
  export declare const ssrGetModuleExport: (id: string) => Promise<any>;
3
- export declare const ssrWebpackRequire: ((id: string) => Promise<{
3
+ export declare const ssrWebpackRequire: (id: string) => Promise<{
4
4
  [x: string]: any;
5
- }>) & import("lodash").MemoizedFunction;
5
+ }>;
@@ -1,5 +1,5 @@
1
- import memoize from "lodash/memoize";
2
- export const ssrLoadModule = memoize(async (id) => {
1
+ import { memoizeOnId } from "../lib/memoizeOnId";
2
+ export const ssrLoadModule = memoizeOnId(async (id) => {
3
3
  const { useClientLookup } = await import("virtual:use-client-lookup.js");
4
4
  const moduleFn = useClientLookup[id];
5
5
  if (!moduleFn) {
@@ -13,7 +13,7 @@ export const ssrGetModuleExport = async (id) => {
13
13
  return module[name];
14
14
  };
15
15
  // context(justinvdm, 2 Dec 2024): re memoize(): React relies on the same promise instance being returned for the same id
16
- export const ssrWebpackRequire = memoize(async (id) => {
16
+ export const ssrWebpackRequire = memoizeOnId(async (id) => {
17
17
  const [file, name] = id.split("#");
18
18
  const module = await ssrLoadModule(file);
19
19
  return { [id]: module[name] };
@@ -1,5 +1,5 @@
1
- export declare const loadServerModule: ((id: string) => Promise<any>) & import("lodash").MemoizedFunction;
1
+ export declare const loadServerModule: (id: string) => Promise<any>;
2
2
  export declare const getServerModuleExport: (id: string) => Promise<any>;
3
- export declare const ssrWebpackRequire: ((id: string) => Promise<{
3
+ export declare const ssrWebpackRequire: (id: string) => Promise<{
4
4
  [x: string]: any;
5
- }>) & import("lodash").MemoizedFunction;
5
+ }>;
@@ -1,7 +1,7 @@
1
- import memoize from "lodash/memoize";
2
1
  import { requestInfo } from "../requestInfo/worker";
3
2
  import { ssrWebpackRequire as baseSsrWebpackRequire } from "rwsdk/__ssr_bridge";
4
- export const loadServerModule = memoize(async (id) => {
3
+ import { memoizeOnId } from "../lib/memoizeOnId";
4
+ export const loadServerModule = memoizeOnId(async (id) => {
5
5
  const { useServerLookup } = await import("virtual:use-server-lookup.js");
6
6
  const moduleFn = useServerLookup[id];
7
7
  if (!moduleFn) {
@@ -14,7 +14,7 @@ export const getServerModuleExport = async (id) => {
14
14
  const module = await loadServerModule(file);
15
15
  return module[name];
16
16
  };
17
- export const ssrWebpackRequire = memoize(async (id) => {
17
+ export const ssrWebpackRequire = memoizeOnId(async (id) => {
18
18
  if (!requestInfo.rw.ssr) {
19
19
  return { [id]: () => null };
20
20
  }
@@ -1,2 +1,11 @@
1
- import { type RequestInfo } from "../requestInfo/types";
2
- export declare const getManifest: (requestInfo: RequestInfo) => Promise<Record<string, any>>;
1
+ export type Manifest = Record<string, ManifestChunk>;
2
+ export interface ManifestChunk {
3
+ file: string;
4
+ src?: string;
5
+ isEntry?: boolean;
6
+ isDynamicEntry?: boolean;
7
+ imports?: string[];
8
+ css?: string[];
9
+ assets?: string[];
10
+ }
11
+ export declare const getManifest: () => Promise<Manifest>;
@@ -1,5 +1,5 @@
1
1
  let manifest;
2
- export const getManifest = async (requestInfo) => {
2
+ export const getManifest = async () => {
3
3
  if (manifest) {
4
4
  return manifest;
5
5
  }
@@ -0,0 +1 @@
1
+ export declare const memoizeOnId: <Result>(fn: (id: string) => Result) => (id: string) => Result;
@@ -0,0 +1,11 @@
1
+ export const memoizeOnId = (fn) => {
2
+ const hasOwnProperty = Object.prototype.hasOwnProperty;
3
+ const results = {};
4
+ const memoizedFn = (id) => {
5
+ if (hasOwnProperty.call(results, id)) {
6
+ return results[id];
7
+ }
8
+ return (results[id] = fn(id));
9
+ };
10
+ return memoizedFn;
11
+ };
@@ -1,4 +1,4 @@
1
- import { type Transport } from "../../client";
1
+ import { type Transport } from "../../client/types";
2
2
  export declare const initRealtimeClient: ({ key, handleResponse, }?: {
3
3
  key?: string;
4
4
  handleResponse?: (response: Response) => boolean;
@@ -1,4 +1,4 @@
1
- import { initClient } from "../../client";
1
+ import { initClient } from "../../client/client";
2
2
  import { createFromReadableStream } from "react-server-dom-webpack/client.browser";
3
3
  import { MESSAGE_TYPE } from "./shared";
4
4
  import { packMessage, unpackMessage, } from "./protocol";
@@ -1,3 +1,3 @@
1
- export declare const loadServerModule: ((id: string) => Promise<any>) & import("lodash").MemoizedFunction;
1
+ export declare const loadServerModule: (id: string) => Promise<any>;
2
2
  export declare const getServerModuleExport: (id: string) => Promise<any>;
3
3
  export declare const createServerReference: (id: string, name: string) => any;
@@ -1,6 +1,6 @@
1
- import memoize from "lodash/memoize";
2
1
  import { createServerReference as baseCreateServerReference } from "react-server-dom-webpack/client.edge";
3
- export const loadServerModule = memoize(async (id) => {
2
+ import { memoizeOnId } from "../lib/memoizeOnId";
3
+ export const loadServerModule = memoizeOnId(async (id) => {
4
4
  const { useServerLookup } = await import("virtual:use-server-lookup.js");
5
5
  const moduleFn = useServerLookup[id];
6
6
  if (!moduleFn) {
@@ -0,0 +1,6 @@
1
+ import type { RequestInfo } from "../requestInfo/types.js";
2
+ import type { Manifest, ManifestChunk } from "../lib/manifest.js";
3
+ export declare function findScriptForModule(id: string, manifest: Manifest): ManifestChunk | undefined;
4
+ export declare const Preloads: ({ requestInfo }: {
5
+ requestInfo: RequestInfo;
6
+ }) => import("react/jsx-runtime.js").JSX.Element;
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { use } from "react";
3
+ import { getManifest } from "../lib/manifest.js";
4
+ export function findScriptForModule(id, manifest) {
5
+ const visited = new Set();
6
+ function find(id) {
7
+ if (visited.has(id)) {
8
+ return;
9
+ }
10
+ visited.add(id);
11
+ const manifestEntry = manifest[id];
12
+ if (!manifestEntry) {
13
+ return;
14
+ }
15
+ if (manifestEntry.isEntry || manifestEntry.isDynamicEntry) {
16
+ return manifestEntry;
17
+ }
18
+ if (manifestEntry.imports) {
19
+ for (const dep of manifestEntry.imports) {
20
+ const entry = find(dep);
21
+ if (entry) {
22
+ return entry;
23
+ }
24
+ }
25
+ }
26
+ return;
27
+ }
28
+ return find(id);
29
+ }
30
+ export const Preloads = ({ requestInfo }) => {
31
+ const manifest = use(getManifest());
32
+ const allScripts = new Set();
33
+ for (const scriptId of requestInfo.rw.scriptsToBeLoaded) {
34
+ const script = findScriptForModule(scriptId, manifest);
35
+ if (script) {
36
+ allScripts.add(script.file);
37
+ }
38
+ }
39
+ return (_jsx(_Fragment, { children: Array.from(allScripts).map((href) => (_jsx("link", { rel: "modulepreload", href: href }, href))) }));
40
+ };
@@ -1,12 +1,13 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { use } from "react";
3
3
  import { renderToReadableStream } from "react-dom/server.edge";
4
+ import { Preloads } from "./preloads.js";
4
5
  import { Stylesheets } from "./stylesheets.js";
5
6
  export const renderRscThenableToHtmlStream = async ({ thenable, Document, requestInfo, shouldSSR, onError, }) => {
6
7
  const Component = () => {
7
8
  const RscApp = () => {
8
9
  const node = use(thenable).node;
9
- return (_jsxs(_Fragment, { children: [_jsx(Stylesheets, { requestInfo: requestInfo }), _jsx("div", { id: "hydrate-root", children: node })] }));
10
+ return (_jsxs(_Fragment, { children: [_jsx(Stylesheets, { requestInfo: requestInfo }), _jsx(Preloads, { requestInfo: requestInfo }), _jsx("div", { id: "hydrate-root", children: node })] }));
10
11
  };
11
12
  // todo(justinvdm, 18 Jun 2025): We can build on this later to allow users
12
13
  // surface context. e.g:
@@ -23,7 +23,7 @@ const findCssForModule = (scriptId, manifest) => {
23
23
  return Array.from(css);
24
24
  };
25
25
  export const Stylesheets = ({ requestInfo }) => {
26
- const manifest = use(getManifest(requestInfo));
26
+ const manifest = use(getManifest());
27
27
  const allStylesheets = new Set();
28
28
  for (const scriptId of requestInfo.rw.scriptsToBeLoaded) {
29
29
  const css = findCssForModule(scriptId, manifest);
@@ -9,5 +9,7 @@ export interface RequestInfo<Params = any, AppContext = DefaultAppContext> {
9
9
  headers: Headers;
10
10
  rw: RwContext;
11
11
  cf: ExecutionContext;
12
- response: ResponseInit;
12
+ response: ResponseInit & {
13
+ headers: Headers;
14
+ };
13
15
  }
@@ -169,7 +169,7 @@ export const defineApp = (routes) => {
169
169
  if (e instanceof Response) {
170
170
  return e;
171
171
  }
172
- console.error("Unhandled error", e);
172
+ console.error("rwsdk: Received an unhandled error:\n\n%s", e);
173
173
  throw e;
174
174
  }
175
175
  },
@@ -66,6 +66,9 @@ export const miniflareHMRPlugin = (givenOptions) => [
66
66
  };
67
67
  },
68
68
  async hotUpdate(ctx) {
69
+ if (ctx.file.includes(".wrangler")) {
70
+ return;
71
+ }
69
72
  if (hasErrored) {
70
73
  const shortName = getShortName(ctx.file, ctx.server.config.root);
71
74
  this.environment.logger.info(`${colors.cyan(`attempting to recover from error`)}: update to ${colors.dim(shortName)}`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rwsdk",
3
- "version": "0.2.0-alpha.13",
3
+ "version": "0.2.0-alpha.15",
4
4
  "description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
5
5
  "type": "module",
6
6
  "bin": {
File without changes