vike-react-rsc-rudder 1.0.0

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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/cache-BCYpxG4P.js +164 -0
  4. package/dist/client-BpZo3JRu.js +86 -0
  5. package/dist/client.d.ts +1 -0
  6. package/dist/client.js +53 -0
  7. package/dist/config.d.ts +4 -0
  8. package/dist/config.js +875 -0
  9. package/dist/constants.d.ts +1 -0
  10. package/dist/getGlobalObject-CzWO6NfD.js +10 -0
  11. package/dist/hooks/pageContext/pageContext-client.d.ts +11 -0
  12. package/dist/hooks/pageContext/pageContext-client.js +4 -0
  13. package/dist/hooks/pageContext/pageContext-server.d.ts +7 -0
  14. package/dist/hooks/pageContext/pageContext-server.js +4 -0
  15. package/dist/integration/client.d.ts +1 -0
  16. package/dist/integration/client.js +16 -0
  17. package/dist/integration/getPageElement/getPageElement-server.d.ts +14 -0
  18. package/dist/integration/onBeforeRender.d.ts +2 -0
  19. package/dist/integration/onBeforeRender.js +17 -0
  20. package/dist/integration/onPageTransitionStart.d.ts +2 -0
  21. package/dist/integration/onPageTransitionStart.js +11 -0
  22. package/dist/integration/onRenderClient.d.ts +2 -0
  23. package/dist/integration/onRenderClient.js +63 -0
  24. package/dist/integration/onRenderHtml.d.ts +2 -0
  25. package/dist/integration/onRenderHtml.js +10 -0
  26. package/dist/integration/rscMiddleware.d.ts +3 -0
  27. package/dist/integration/rscMiddleware.js +35 -0
  28. package/dist/pageContext-client-CmvZ4bmk.js +27 -0
  29. package/dist/pageContext-server-B1QwrAws.js +22 -0
  30. package/dist/plugin/index.d.ts +20 -0
  31. package/dist/plugin/plugins/clientDepTrackerPlugin.d.ts +7 -0
  32. package/dist/plugin/plugins/config.d.ts +8 -0
  33. package/dist/plugin/plugins/cssTrackerPlugin.d.ts +6 -0
  34. package/dist/plugin/plugins/dev.d.ts +2 -0
  35. package/dist/plugin/plugins/hmrPlugin.d.ts +2 -0
  36. package/dist/plugin/plugins/injectManifestBuild.d.ts +6 -0
  37. package/dist/plugin/plugins/serverComponentExclusionPlugin.d.ts +6 -0
  38. package/dist/plugin/plugins/useClientPlugin.d.ts +2 -0
  39. package/dist/plugin/plugins/useServerPlugin.d.ts +2 -0
  40. package/dist/plugin/plugins/virtuals.d.ts +2 -0
  41. package/dist/plugin/utils.d.ts +6 -0
  42. package/dist/register/browser.d.ts +1 -0
  43. package/dist/register/browser.js +3 -0
  44. package/dist/register/server.d.ts +1 -0
  45. package/dist/register/server.js +3 -0
  46. package/dist/register/ssr.d.ts +1 -0
  47. package/dist/register/ssr.js +3 -0
  48. package/dist/runtime/cache.d.ts +50 -0
  49. package/dist/runtime/client/globalState.d.ts +22 -0
  50. package/dist/runtime/client.d.ts +5 -0
  51. package/dist/runtime/rscBridge.d.ts +7 -0
  52. package/dist/runtime/server.d.ts +7 -0
  53. package/dist/runtime/server.js +110 -0
  54. package/dist/runtime/serverActionContext.d.ts +23 -0
  55. package/dist/runtime/ssr.d.ts +2 -0
  56. package/dist/runtime/ssr.js +107 -0
  57. package/dist/server.d.ts +1 -0
  58. package/dist/server.js +4 -0
  59. package/dist/serverActionContext-7Jnbh0-W.js +38 -0
  60. package/dist/types/Config.d.ts +59 -0
  61. package/dist/types.d.ts +22 -0
  62. package/dist/utils/getGlobalObject.d.ts +1 -0
  63. package/dist/utils/isReactElement.d.ts +5 -0
  64. package/package.json +81 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) nitedani and vike-react-rsc contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # `vike-react-rsc-rudder`
2
+
3
+ React Server Components support for [Vike](https://vike.dev). This is a
4
+ **RudderJS-maintained fork** of [`vike-react-rsc`](https://github.com/nitedani/vike-react-rsc)
5
+ by **nitedani**, **MIT-licensed** (see `LICENSE`). Copyright for the original
6
+ work remains with nitedani and contributors; the fork's changes are documented
7
+ below.
8
+
9
+ ## Why this fork exists
10
+
11
+ The working version of the upstream package (`1.0.0`) lives **only** in the
12
+ GitHub repository — npm has nothing but a `0.0.0` stub published in 2024-04, and
13
+ the upstream "Publish?" request ([issue #3](https://github.com/nitedani/vike-react-rsc/issues/3),
14
+ 2025-11-13) is unanswered. RudderJS's RSC integration needs an installable,
15
+ versioned package, so we publish this maintained fork.
16
+
17
+ ### Why the name isn't `@rudderjs/…`
18
+
19
+ Vike's config-extension mechanism only recognizes an extension's `extends`
20
+ import (and turns it into a pointer import) when the package name **starts with
21
+ `vike-`** — see `path.startsWith('vike-') && path.endsWith('/config')` in vike's
22
+ `transpileWithEsbuild`. A scoped `@rudderjs/…` name isn't detected, so apps
23
+ would have to add a `with { type: 'vike:pointer' }` attribute to the `extends`
24
+ import in every `+config.ts`. Keeping the unscoped `vike-*` convention (as
25
+ `vike-react`, `vike-vue`, … all do) means RSC apps wire it up with a plain
26
+ `extends: [vikeReactRsc]` and no per-app ceremony. The `-rudder` suffix marks
27
+ it as our fork and distinguishes it from upstream's `vike-react-rsc`.
28
+
29
+ It is consumed by `@rudderjs/vite`'s view scanner, which detects either
30
+ `vike-react-rsc-rudder` (preferred) or the legacy upstream `vike-react-rsc` name
31
+ and generates RSC page stubs that import from whichever is installed.
32
+
33
+ ## Source
34
+
35
+ - Upstream: https://github.com/nitedani/vike-react-rsc
36
+ - Forked at commit: `094054c7649d70707b8f5749ca3e22e33a67801f` (2025-12-06,
37
+ upstream `main` HEAD as of forking)
38
+
39
+ ## Changes from upstream
40
+
41
+ - `package.json`: renamed to `vike-react-rsc-rudder`; published publicly
42
+ (`publishConfig.access: public`); `@vitejs/plugin-rsc` moved from
43
+ `devDependencies` to `dependencies` (the build externalizes it, so consumers
44
+ need it at runtime); `react` / `react-dom` / `vite` / `vike` dev pins aligned
45
+ to the RudderJS monorepo versions.
46
+ - `src/constants.ts` + `src/config.ts`: the package self-references
47
+ (`PKG_NAME`, the vike config `name`, and the `import:…/__internal/…`
48
+ specifiers) updated to the fork package name.
49
+ - `src/config.ts`: config import strings name the export (`:default`) — vike
50
+ ≥0.4.257 requires it; the upstream 1.0.0 (built against 0.4.246) omitted it,
51
+ which crashed vike's dev `optimizeDeps`.
52
+ - `src/config.ts`: the SSR build's `rollupOptions.input` also includes Vike's
53
+ server entry (`entry: serverEntryVirtualId`). Upstream targets `vike-server`
54
+ (no `+server.ts`); RudderJS uses the `+server.ts` → `app.fetch` model, where
55
+ `@brillout/vite-plugin-server-entry` needs that entry — otherwise the
56
+ production build fails with "Cannot find build server entry".
57
+ - `src/plugin/plugins/injectManifestBuild.ts`: the page-entry virtual id was
58
+ renamed by vike (`virtual:vike:pageConfigValuesAll:server:` →
59
+ `virtual:vike:page-entry:server:`, vike ≥0.4.257). Without the rename the
60
+ production RSC manifest is empty and rendering 500s ("Cannot read properties
61
+ of undefined (reading 'getConfig')"). The module shape
62
+ (`configValuesSerialized`) is unchanged.
63
+ - Added this file and `LICENSE` (the upstream repo ships no `LICENSE` file
64
+ despite declaring MIT in `package.json`).
65
+
66
+ Source is otherwise unmodified. Build: `tsdown --clean` (→ `dist/`). The vike
67
+ version-compat changes above (config import strings, the page-entry virtual id)
68
+ should be re-checked whenever vike is bumped.
69
+
70
+ ## Re-syncing from upstream
71
+
72
+ Re-copy `src/`, `tsdown.config.ts`, and `tsconfig.json` from a newer upstream
73
+ commit, then re-apply the changes above. Update the commit hash in this file.
@@ -0,0 +1,164 @@
1
+ import { getGlobalObject } from "./getGlobalObject-CzWO6NfD.js";
2
+
3
+ //#region src/runtime/client/globalState.ts
4
+ function getGlobalClientState() {
5
+ return getGlobalObject("globalState.ts", {
6
+ rscCache: new Map(),
7
+ serverComponentCache: new Map(),
8
+ pendingRequests: new Map(),
9
+ isRscCall: false
10
+ });
11
+ }
12
+
13
+ //#endregion
14
+ //#region src/runtime/cache.ts
15
+ const DEFAULT_STALE_TIME = 60 * 1e3;
16
+ /**
17
+ * Get the cache key for a page context
18
+ */
19
+ function getCacheKey(pageContext) {
20
+ return `${pageContext.urlPathname}${pageContext.urlParsed.searchOriginal || ""}`;
21
+ }
22
+ /**
23
+ * Get stale time from page context
24
+ */
25
+ function getStaleTime(pageContext) {
26
+ const userConfig = pageContext.config?.rsc;
27
+ return userConfig?.staleTime !== void 0 ? userConfig.staleTime : DEFAULT_STALE_TIME;
28
+ }
29
+ /**
30
+ * Get a cached entry if it exists and is not stale
31
+ */
32
+ function getCachedPayload(pageContext) {
33
+ if (typeof window === "undefined") return null;
34
+ const staleTime = getStaleTime(pageContext);
35
+ if (staleTime === 0) return null;
36
+ const globalState = getGlobalClientState();
37
+ const cacheKey = getCacheKey(pageContext);
38
+ const cachedEntry = globalState.rscCache.get(cacheKey);
39
+ if (cachedEntry && Date.now() - cachedEntry.timestamp < staleTime) {
40
+ console.log("[RSC Cache] Using cached payload for", cacheKey);
41
+ return cachedEntry.payload;
42
+ }
43
+ return null;
44
+ }
45
+ /**
46
+ * Store a payload in the cache
47
+ */
48
+ function cachePayload(pageContext, payload) {
49
+ if (typeof window === "undefined") return;
50
+ const staleTime = getStaleTime(pageContext);
51
+ if (staleTime === 0) return;
52
+ const globalState = getGlobalClientState();
53
+ const cacheKey = getCacheKey(pageContext);
54
+ globalState.rscCache.set(cacheKey, {
55
+ payload,
56
+ timestamp: Date.now()
57
+ });
58
+ console.log("[RSC Cache] Stored payload for", cacheKey);
59
+ }
60
+ /**
61
+ * Invalidate the cache entry for a specific page
62
+ */
63
+ function invalidateCache(pageContext) {
64
+ if (typeof window === "undefined") return;
65
+ const globalState = getGlobalClientState();
66
+ const cacheKey = getCacheKey(pageContext);
67
+ if (globalState.rscCache.has(cacheKey)) {
68
+ globalState.rscCache.delete(cacheKey);
69
+ console.log("[RSC Cache] Invalidated main cache for", cacheKey);
70
+ }
71
+ }
72
+ /**
73
+ * Mark all server component cache entries as stale
74
+ * This is useful when a server action changes data that server components depend on
75
+ */
76
+ function invalidateServerComponentCache() {
77
+ if (typeof window === "undefined") return;
78
+ const globalState = getGlobalClientState();
79
+ if (globalState.serverComponentCache.size > 0) {
80
+ let staleCount = 0;
81
+ globalState.serverComponentCache.forEach((entry) => {
82
+ if (!entry.isStale) {
83
+ entry.isStale = true;
84
+ staleCount++;
85
+ }
86
+ });
87
+ if (staleCount > 0) console.log(`[RSC Cache] Marked ${staleCount} server component cache entries as stale`);
88
+ }
89
+ }
90
+ /**
91
+ * Clear all pending server component requests
92
+ * This is useful when navigating between pages or when invalidating the cache
93
+ */
94
+ function clearPendingServerComponentRequests() {
95
+ if (typeof window === "undefined") return;
96
+ const globalState = getGlobalClientState();
97
+ globalState.pendingRequests.clear();
98
+ console.log("[RSC Cache] Cleared pending server component requests");
99
+ }
100
+ /**
101
+ * Get a cached server component if it exists
102
+ * Returns the component even if it's stale (stale-while-revalidate pattern)
103
+ * The caller should check the isStale flag and trigger a revalidation if needed
104
+ */
105
+ function getCachedServerComponent(key, pageContext) {
106
+ if (typeof window === "undefined") return {
107
+ component: null,
108
+ isStale: false
109
+ };
110
+ const staleTime = getStaleTime(pageContext);
111
+ const globalState = getGlobalClientState();
112
+ const cachedEntry = globalState.serverComponentCache.get(key);
113
+ if (staleTime === 0) return {
114
+ component: null,
115
+ isStale: false
116
+ };
117
+ if (!cachedEntry) return {
118
+ component: null,
119
+ isStale: false
120
+ };
121
+ const componentName = key.split("-")[0];
122
+ const isExplicitlyStale = cachedEntry.isStale === true;
123
+ const isTimeStale = Date.now() - cachedEntry.timestamp >= staleTime;
124
+ const isStale = isExplicitlyStale || isTimeStale;
125
+ if (isStale) console.log(`[RSC Cache] Using stale server component: ${componentName}`);
126
+ else console.log(`[RSC Cache] Using fresh server component: ${componentName}`);
127
+ return {
128
+ component: cachedEntry.payload.returnValue,
129
+ isStale
130
+ };
131
+ }
132
+ /**
133
+ * Mark a server component as being revalidated
134
+ */
135
+ function markServerComponentRevalidating(key) {
136
+ if (typeof window === "undefined") return;
137
+ const globalState = getGlobalClientState();
138
+ const cachedEntry = globalState.serverComponentCache.get(key);
139
+ if (cachedEntry) {
140
+ cachedEntry.revalidating = true;
141
+ const componentName = key.split("-")[0];
142
+ console.log(`[RSC Cache] Revalidating server component: ${componentName}`);
143
+ }
144
+ }
145
+ /**
146
+ * Store a server component in the cache
147
+ */
148
+ function cacheServerComponent(key, component, pageContext) {
149
+ if (typeof window === "undefined") return;
150
+ const staleTime = getStaleTime(pageContext);
151
+ if (staleTime === 0) return;
152
+ const globalState = getGlobalClientState();
153
+ globalState.serverComponentCache.set(key, {
154
+ payload: { returnValue: component },
155
+ timestamp: Date.now(),
156
+ isStale: false,
157
+ revalidating: false
158
+ });
159
+ const componentName = key.split("-")[0];
160
+ console.log(`[RSC Cache] Stored fresh server component: ${componentName}`);
161
+ }
162
+
163
+ //#endregion
164
+ export { cachePayload, cacheServerComponent, clearPendingServerComponentRequests, getCachedPayload, getCachedServerComponent, getGlobalClientState, invalidateCache, invalidateServerComponentCache, markServerComponentRevalidating };
@@ -0,0 +1,86 @@
1
+ import { cachePayload, clearPendingServerComponentRequests, getCachedPayload, getGlobalClientState, invalidateCache, invalidateServerComponentCache } from "./cache-BCYpxG4P.js";
2
+ import { startTransition } from "react";
3
+ import envName from "virtual:enviroment-name";
4
+ import { tinyassert } from "@hiogawa/utils";
5
+ import * as ReactClient from "@vitejs/plugin-rsc/react/browser";
6
+
7
+ //#region src/runtime/client.tsx
8
+ tinyassert(envName === "client", "Invalid environment");
9
+ function getVikeUrlOriginal(pageContext) {
10
+ return `${pageContext.urlPathname === "/" ? "" : pageContext.urlPathname}/index.pageContext.json${pageContext.urlParsed.searchOriginal || ""}`;
11
+ }
12
+ async function callServer(id, args) {
13
+ const globalState = getGlobalClientState();
14
+ const isRscCall = globalState.isRscCall;
15
+ console.log("[RSC Client] Calling server action:", id, isRscCall ? "(from server component)" : "");
16
+ const result = await ReactClient.createFromFetch(fetch("/_rsc", {
17
+ method: "POST",
18
+ headers: {
19
+ "x-rsc-action": id,
20
+ "x-vike-urloriginal": getVikeUrlOriginal(globalState.pageContext),
21
+ ...isRscCall ? { "x-rsc-component-call": "true" } : {}
22
+ },
23
+ body: await ReactClient.encodeReply(args)
24
+ }));
25
+ if (result.root) {
26
+ console.log("[RSC Client] Server action triggered re-render");
27
+ startTransition(() => {
28
+ globalState.setPayload?.((current) => {
29
+ cachePayload(current.pageContext, result);
30
+ return {
31
+ pageContext: current.pageContext,
32
+ payload: result
33
+ };
34
+ });
35
+ });
36
+ } else {
37
+ console.log("[RSC Client] Server action returned without re-render");
38
+ if (!isRscCall && typeof window !== "undefined") {
39
+ if (globalState.pageContext) invalidateCache(globalState.pageContext);
40
+ }
41
+ }
42
+ if (!isRscCall) invalidateServerComponentCache();
43
+ return result.returnValue;
44
+ }
45
+ ReactClient.setServerCallback(callServer);
46
+ if (import.meta.hot) import.meta.hot.on("rsc:update", async () => {
47
+ const globalState = getGlobalClientState();
48
+ invalidateCache(getGlobalClientState().pageContext);
49
+ invalidateServerComponentCache();
50
+ const payload = await onNavigate(globalState.pageContext);
51
+ globalState.setPayload?.((current) => {
52
+ return {
53
+ pageContext: current.pageContext,
54
+ payload
55
+ };
56
+ });
57
+ });
58
+ function onNavigate(pageContext) {
59
+ console.log("[RSC Client] Navigation:", pageContext.urlPathname);
60
+ const globalState = getGlobalClientState();
61
+ clearPendingServerComponentRequests();
62
+ const cachedPayload = getCachedPayload(pageContext);
63
+ if (cachedPayload) {
64
+ globalState.navigationPromise = Promise.resolve(cachedPayload);
65
+ return Promise.resolve(cachedPayload);
66
+ }
67
+ console.log("[RSC Client] Fetching RSC payload for", pageContext.urlPathname);
68
+ const fetchPromise = ReactClient.createFromFetch(fetch("/_rsc", {
69
+ method: "GET",
70
+ headers: { "x-vike-urloriginal": getVikeUrlOriginal(pageContext) }
71
+ }));
72
+ globalState.navigationPromise = fetchPromise;
73
+ fetchPromise.then((payload) => {
74
+ cachePayload(pageContext, payload);
75
+ });
76
+ return fetchPromise;
77
+ }
78
+ async function parseRscStream(stream) {
79
+ console.log("[RSC Client] Parsing RSC stream...");
80
+ const initialPayload = await ReactClient.createFromReadableStream(stream);
81
+ console.log("[RSC Client] RSC stream parsed");
82
+ return initialPayload;
83
+ }
84
+
85
+ //#endregion
86
+ export { onNavigate, parseRscStream };
@@ -0,0 +1 @@
1
+ export { rsc } from "./runtime/rscBridge.js";
package/dist/client.js ADDED
@@ -0,0 +1,53 @@
1
+ import "./getGlobalObject-CzWO6NfD.js";
2
+ import { usePageContext } from "./pageContext-client-CmvZ4bmk.js";
3
+ import { cacheServerComponent, getCachedServerComponent, getGlobalClientState, markServerComponentRevalidating } from "./cache-BCYpxG4P.js";
4
+ import React, { useEffect } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+
7
+ //#region src/runtime/rscBridge.tsx
8
+ function rsc(c) {
9
+ return (props) => {
10
+ const pageContext = usePageContext();
11
+ const { fallback,...rest } = props;
12
+ const Loading = pageContext.config.Loading?.component || (() => null);
13
+ const fallback_ = fallback ?? /* @__PURE__ */ jsx(Loading, {});
14
+ const cacheKey = `${c.name}-${JSON.stringify(rest)}`;
15
+ const { component: cachedComponent, isStale } = getCachedServerComponent(cacheKey, pageContext);
16
+ const [comp, setComp] = React.useState(cachedComponent);
17
+ useEffect(() => {
18
+ const globalState = getGlobalClientState();
19
+ const fetchOrRevalidate = () => {
20
+ const pendingRequest = globalState.pendingRequests.get(cacheKey);
21
+ if (pendingRequest) {
22
+ console.log(`[RSC Client] Reusing pending request for ${c.name || "UnknownComponent"}`, rest);
23
+ pendingRequest.then((result) => {
24
+ setComp(result);
25
+ });
26
+ } else {
27
+ console.log(`[RSC Client] ${cachedComponent ? "Revalidating" : "Fetching"} server component ${c.name || "UnknownComponent"}`, rest);
28
+ if (cachedComponent) markServerComponentRevalidating(cacheKey);
29
+ globalState.isRscCall = true;
30
+ const serverComponentPromise = c(rest);
31
+ globalState.isRscCall = false;
32
+ const requestPromise = serverComponentPromise.then((result) => {
33
+ cacheServerComponent(cacheKey, result, pageContext);
34
+ globalState.pendingRequests.delete(cacheKey);
35
+ return result;
36
+ }).catch((error) => {
37
+ console.error("[RSC Client] Error fetching server component:", error);
38
+ globalState.pendingRequests.delete(cacheKey);
39
+ throw error;
40
+ });
41
+ globalState.pendingRequests.set(cacheKey, requestPromise);
42
+ requestPromise.then(setComp);
43
+ }
44
+ };
45
+ if (!cachedComponent || isStale) fetchOrRevalidate();
46
+ }, []);
47
+ if (!comp) return fallback_;
48
+ return comp;
49
+ };
50
+ }
51
+
52
+ //#endregion
53
+ export { rsc };
@@ -0,0 +1,4 @@
1
+ export { config as default };
2
+ import type { Config } from "vike/types";
3
+ declare const config: Config;
4
+ import "./types/Config.js";