react-on-rails-pro 16.2.0-beta.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.
- package/lib/CallbackRegistry.d.ts +18 -0
- package/lib/CallbackRegistry.js +112 -0
- package/lib/ClientSideRenderer.d.ts +8 -0
- package/lib/ClientSideRenderer.js +238 -0
- package/lib/ComponentRegistry.d.ts +22 -0
- package/lib/ComponentRegistry.js +57 -0
- package/lib/PostSSRHookTracker.d.ts +32 -0
- package/lib/PostSSRHookTracker.js +70 -0
- package/lib/RSCProvider.d.ts +52 -0
- package/lib/RSCProvider.js +90 -0
- package/lib/RSCRequestTracker.d.ts +75 -0
- package/lib/RSCRequestTracker.js +130 -0
- package/lib/RSCRoute.d.ts +30 -0
- package/lib/RSCRoute.js +57 -0
- package/lib/ReactOnRails.client.d.ts +4 -0
- package/lib/ReactOnRails.client.js +20 -0
- package/lib/ReactOnRails.full.d.ts +4 -0
- package/lib/ReactOnRails.full.js +26 -0
- package/lib/ReactOnRails.node.d.ts +3 -0
- package/lib/ReactOnRails.node.js +21 -0
- package/lib/ReactOnRailsRSC.d.ts +4 -0
- package/lib/ReactOnRailsRSC.js +81 -0
- package/lib/ServerComponentFetchError.d.ts +15 -0
- package/lib/ServerComponentFetchError.js +33 -0
- package/lib/StoreRegistry.d.ts +59 -0
- package/lib/StoreRegistry.js +108 -0
- package/lib/createReactOnRailsPro.d.ts +7 -0
- package/lib/createReactOnRailsPro.js +111 -0
- package/lib/getReactServerComponent.client.d.ts +47 -0
- package/lib/getReactServerComponent.client.js +156 -0
- package/lib/getReactServerComponent.server.d.ts +39 -0
- package/lib/getReactServerComponent.server.js +70 -0
- package/lib/handleError.d.ts +5 -0
- package/lib/handleError.js +8 -0
- package/lib/handleErrorRSC.d.ts +4 -0
- package/lib/handleErrorRSC.js +11 -0
- package/lib/injectRSCPayload.d.ts +31 -0
- package/lib/injectRSCPayload.js +276 -0
- package/lib/loadJsonFile.d.ts +4 -0
- package/lib/loadJsonFile.js +36 -0
- package/lib/registerServerComponent/client.d.ts +33 -0
- package/lib/registerServerComponent/client.js +43 -0
- package/lib/registerServerComponent/server.d.ts +22 -0
- package/lib/registerServerComponent/server.js +31 -0
- package/lib/registerServerComponent/server.rsc.d.ts +24 -0
- package/lib/registerServerComponent/server.rsc.js +37 -0
- package/lib/streamServerRenderedReactComponent.d.ts +5 -0
- package/lib/streamServerRenderedReactComponent.js +76 -0
- package/lib/streamingUtils.d.ts +34 -0
- package/lib/streamingUtils.js +201 -0
- package/lib/transformRSCNodeStream.d.ts +16 -0
- package/lib/transformRSCNodeStream.js +56 -0
- package/lib/transformRSCStreamAndReplayConsoleLogs.d.ts +16 -0
- package/lib/transformRSCStreamAndReplayConsoleLogs.js +83 -0
- package/lib/utils.d.ts +26 -0
- package/lib/utils.js +43 -0
- package/lib/wrapServerComponentRenderer/client.d.ts +23 -0
- package/lib/wrapServerComponentRenderer/client.js +80 -0
- package/lib/wrapServerComponentRenderer/server.d.ts +23 -0
- package/lib/wrapServerComponentRenderer/server.js +59 -0
- package/lib/wrapServerComponentRenderer/server.rsc.d.ts +3 -0
- package/lib/wrapServerComponentRenderer/server.rsc.js +19 -0
- package/package.json +83 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Shakacode LLC
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT licensed under the MIT (open source) license.
|
|
5
|
+
* It is part of the React on Rails Pro offering and is licensed separately.
|
|
6
|
+
*
|
|
7
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
8
|
+
* via any medium, is strictly prohibited without a valid license agreement
|
|
9
|
+
* from Shakacode LLC.
|
|
10
|
+
*
|
|
11
|
+
* For licensing terms, please see:
|
|
12
|
+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
|
+
*/
|
|
14
|
+
'use client';
|
|
15
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
16
|
+
import { createContext, useContext } from 'react';
|
|
17
|
+
import { createRSCPayloadKey } from "./utils.js";
|
|
18
|
+
const RSCContext = createContext(undefined);
|
|
19
|
+
/**
|
|
20
|
+
* Creates a provider context for React Server Components.
|
|
21
|
+
*
|
|
22
|
+
* RSCProvider is a foundational component that:
|
|
23
|
+
* 1. Provides caching for server components to prevent redundant requests
|
|
24
|
+
* 2. Manages the fetching of server components through getComponent
|
|
25
|
+
* 3. Offers environment-agnostic access to server components
|
|
26
|
+
*
|
|
27
|
+
* This factory function accepts an environment-specific getServerComponent implementation,
|
|
28
|
+
* allowing it to work correctly in both client and server environments.
|
|
29
|
+
*
|
|
30
|
+
* @param railsContext - Context for the current request
|
|
31
|
+
* @param getServerComponent - Environment-specific function for fetching server components
|
|
32
|
+
* @returns A provider component that wraps children with RSC context
|
|
33
|
+
*
|
|
34
|
+
* @important This is an internal function. End users should not use this directly.
|
|
35
|
+
* Instead, use wrapServerComponentRenderer from 'react-on-rails/wrapServerComponentRenderer/client'
|
|
36
|
+
* for client-side rendering or 'react-on-rails/wrapServerComponentRenderer/server' for server-side rendering.
|
|
37
|
+
*/
|
|
38
|
+
export const createRSCProvider = ({ getServerComponent, }) => {
|
|
39
|
+
const fetchRSCPromises = {};
|
|
40
|
+
const getComponent = (componentName, componentProps) => {
|
|
41
|
+
const key = createRSCPayloadKey(componentName, componentProps);
|
|
42
|
+
if (key in fetchRSCPromises) {
|
|
43
|
+
return fetchRSCPromises[key];
|
|
44
|
+
}
|
|
45
|
+
const promise = getServerComponent({ componentName, componentProps });
|
|
46
|
+
fetchRSCPromises[key] = promise;
|
|
47
|
+
return promise;
|
|
48
|
+
};
|
|
49
|
+
const refetchComponent = (componentName, componentProps) => {
|
|
50
|
+
const key = createRSCPayloadKey(componentName, componentProps);
|
|
51
|
+
const promise = getServerComponent({
|
|
52
|
+
componentName,
|
|
53
|
+
componentProps,
|
|
54
|
+
enforceRefetch: true,
|
|
55
|
+
});
|
|
56
|
+
fetchRSCPromises[key] = promise;
|
|
57
|
+
return promise;
|
|
58
|
+
};
|
|
59
|
+
const contextValue = { getComponent, refetchComponent };
|
|
60
|
+
return ({ children }) => {
|
|
61
|
+
return _jsx(RSCContext.Provider, { value: contextValue, children: children });
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Hook to access the RSC context within client components.
|
|
66
|
+
*
|
|
67
|
+
* This hook provides access to:
|
|
68
|
+
* - getComponent: For fetching and rendering server components
|
|
69
|
+
* - refetchComponent: For refetching server components
|
|
70
|
+
*
|
|
71
|
+
* It must be used within a component wrapped by RSCProvider (typically done
|
|
72
|
+
* automatically by wrapServerComponentRenderer).
|
|
73
|
+
*
|
|
74
|
+
* @returns The RSC context containing methods for working with server components
|
|
75
|
+
* @throws Error if used outside of an RSCProvider
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```tsx
|
|
79
|
+
* const { getComponent } = useRSC();
|
|
80
|
+
* const serverComponent = use(getComponent('MyServerComponent', props));
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export const useRSC = () => {
|
|
84
|
+
const context = useContext(RSCContext);
|
|
85
|
+
if (!context) {
|
|
86
|
+
throw new Error('useRSC must be used within a RSCProvider');
|
|
87
|
+
}
|
|
88
|
+
return context;
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=RSCProvider.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { RSCPayloadStreamInfo, RSCPayloadCallback, RailsContextWithServerComponentMetadata } from 'react-on-rails/types';
|
|
2
|
+
/**
|
|
3
|
+
* Global function provided by React on Rails Pro for generating RSC payloads.
|
|
4
|
+
*
|
|
5
|
+
* This function is injected into the global scope during server-side rendering
|
|
6
|
+
* by the RORP rendering request. It handles the actual generation of React Server
|
|
7
|
+
* Component payloads on the server side.
|
|
8
|
+
*
|
|
9
|
+
* @see https://github.com/shakacode/react_on_rails_pro/blob/master/lib/react_on_rails_pro/server_rendering_js_code.rb
|
|
10
|
+
*/
|
|
11
|
+
declare global {
|
|
12
|
+
function generateRSCPayload(componentName: string, props: unknown, railsContext: RailsContextWithServerComponentMetadata): Promise<NodeJS.ReadableStream>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* RSC Request Tracker - manages RSC payload generation and tracking for a single request.
|
|
16
|
+
*
|
|
17
|
+
* This class provides a local alternative to the global RSC payload management,
|
|
18
|
+
* allowing each request to have its own isolated tracker without sharing state.
|
|
19
|
+
* It includes both tracking functionality for the server renderer and fetching
|
|
20
|
+
* functionality for components.
|
|
21
|
+
*/
|
|
22
|
+
declare class RSCRequestTracker {
|
|
23
|
+
private streams;
|
|
24
|
+
private callbacks;
|
|
25
|
+
private railsContext;
|
|
26
|
+
constructor(railsContext: RailsContextWithServerComponentMetadata);
|
|
27
|
+
/**
|
|
28
|
+
* Clears all streams and callbacks for this request.
|
|
29
|
+
* Should be called when the request is complete to ensure proper cleanup,
|
|
30
|
+
* though garbage collection will handle cleanup automatically when the tracker goes out of scope.
|
|
31
|
+
*
|
|
32
|
+
* This method is safe to call multiple times and will handle any errors during cleanup gracefully.
|
|
33
|
+
*/
|
|
34
|
+
clear(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Registers a callback to be executed when RSC payloads are generated.
|
|
37
|
+
*
|
|
38
|
+
* This function:
|
|
39
|
+
* 1. Stores the callback function for this tracker
|
|
40
|
+
* 2. Immediately executes the callback for any existing streams
|
|
41
|
+
*
|
|
42
|
+
* This synchronous execution is critical for preventing hydration race conditions.
|
|
43
|
+
* It ensures payload array initialization happens before component HTML appears
|
|
44
|
+
* in the response stream.
|
|
45
|
+
*
|
|
46
|
+
* @param callback - Function to call when an RSC payload is generated
|
|
47
|
+
*/
|
|
48
|
+
onRSCPayloadGenerated(callback: RSCPayloadCallback): void;
|
|
49
|
+
/**
|
|
50
|
+
* Generates and tracks RSC payloads for server components.
|
|
51
|
+
*
|
|
52
|
+
* getRSCPayloadStream:
|
|
53
|
+
* 1. Calls the provided generateRSCPayload function
|
|
54
|
+
* 2. Tracks streams in this tracker for later access
|
|
55
|
+
* 3. Notifies callbacks immediately to enable early payload embedding
|
|
56
|
+
*
|
|
57
|
+
* The immediate callback notification is critical for preventing hydration race conditions,
|
|
58
|
+
* as it ensures the payload array is initialized in the HTML stream before component rendering.
|
|
59
|
+
*
|
|
60
|
+
* @param componentName - Name of the server component
|
|
61
|
+
* @param props - Props for the server component
|
|
62
|
+
* @returns A stream of the RSC payload
|
|
63
|
+
* @throws Error if generateRSCPayload is not available or fails
|
|
64
|
+
*/
|
|
65
|
+
getRSCPayloadStream(componentName: string, props: unknown): Promise<NodeJS.ReadableStream>;
|
|
66
|
+
/**
|
|
67
|
+
* Returns all RSC payload streams tracked by this request tracker.
|
|
68
|
+
* Used by the server renderer to access all fetched RSCs for this request.
|
|
69
|
+
*
|
|
70
|
+
* @returns Array of RSC payload stream information
|
|
71
|
+
*/
|
|
72
|
+
getRSCPayloadStreams(): RSCPayloadStreamInfo[];
|
|
73
|
+
}
|
|
74
|
+
export default RSCRequestTracker;
|
|
75
|
+
//# sourceMappingURL=RSCRequestTracker.d.ts.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Shakacode LLC
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT licensed under the MIT (open source) license.
|
|
5
|
+
* It is part of the React on Rails Pro offering and is licensed separately.
|
|
6
|
+
*
|
|
7
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
8
|
+
* via any medium, is strictly prohibited without a valid license agreement
|
|
9
|
+
* from Shakacode LLC.
|
|
10
|
+
*
|
|
11
|
+
* For licensing terms, please see:
|
|
12
|
+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
|
+
*/
|
|
14
|
+
import { PassThrough } from 'stream';
|
|
15
|
+
import { extractErrorMessage } from "./utils.js";
|
|
16
|
+
/**
|
|
17
|
+
* RSC Request Tracker - manages RSC payload generation and tracking for a single request.
|
|
18
|
+
*
|
|
19
|
+
* This class provides a local alternative to the global RSC payload management,
|
|
20
|
+
* allowing each request to have its own isolated tracker without sharing state.
|
|
21
|
+
* It includes both tracking functionality for the server renderer and fetching
|
|
22
|
+
* functionality for components.
|
|
23
|
+
*/
|
|
24
|
+
class RSCRequestTracker {
|
|
25
|
+
constructor(railsContext) {
|
|
26
|
+
this.streams = [];
|
|
27
|
+
this.callbacks = [];
|
|
28
|
+
this.railsContext = railsContext;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Clears all streams and callbacks for this request.
|
|
32
|
+
* Should be called when the request is complete to ensure proper cleanup,
|
|
33
|
+
* though garbage collection will handle cleanup automatically when the tracker goes out of scope.
|
|
34
|
+
*
|
|
35
|
+
* This method is safe to call multiple times and will handle any errors during cleanup gracefully.
|
|
36
|
+
*/
|
|
37
|
+
clear() {
|
|
38
|
+
// Close any active streams before clearing
|
|
39
|
+
this.streams.forEach(({ stream, componentName }, index) => {
|
|
40
|
+
try {
|
|
41
|
+
if (stream && typeof stream.destroy === 'function') {
|
|
42
|
+
stream.destroy();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
// Log the error but don't throw to avoid disrupting cleanup of other streams
|
|
47
|
+
console.warn(`Warning: Error while destroying RSC stream for component "${componentName}" at index ${index}:`, error);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
this.streams = [];
|
|
51
|
+
this.callbacks = [];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Registers a callback to be executed when RSC payloads are generated.
|
|
55
|
+
*
|
|
56
|
+
* This function:
|
|
57
|
+
* 1. Stores the callback function for this tracker
|
|
58
|
+
* 2. Immediately executes the callback for any existing streams
|
|
59
|
+
*
|
|
60
|
+
* This synchronous execution is critical for preventing hydration race conditions.
|
|
61
|
+
* It ensures payload array initialization happens before component HTML appears
|
|
62
|
+
* in the response stream.
|
|
63
|
+
*
|
|
64
|
+
* @param callback - Function to call when an RSC payload is generated
|
|
65
|
+
*/
|
|
66
|
+
onRSCPayloadGenerated(callback) {
|
|
67
|
+
this.callbacks.push(callback);
|
|
68
|
+
// Call callback for any existing streams
|
|
69
|
+
this.streams.forEach(callback);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Generates and tracks RSC payloads for server components.
|
|
73
|
+
*
|
|
74
|
+
* getRSCPayloadStream:
|
|
75
|
+
* 1. Calls the provided generateRSCPayload function
|
|
76
|
+
* 2. Tracks streams in this tracker for later access
|
|
77
|
+
* 3. Notifies callbacks immediately to enable early payload embedding
|
|
78
|
+
*
|
|
79
|
+
* The immediate callback notification is critical for preventing hydration race conditions,
|
|
80
|
+
* as it ensures the payload array is initialized in the HTML stream before component rendering.
|
|
81
|
+
*
|
|
82
|
+
* @param componentName - Name of the server component
|
|
83
|
+
* @param props - Props for the server component
|
|
84
|
+
* @returns A stream of the RSC payload
|
|
85
|
+
* @throws Error if generateRSCPayload is not available or fails
|
|
86
|
+
*/
|
|
87
|
+
async getRSCPayloadStream(componentName, props) {
|
|
88
|
+
// Validate that the global generateRSCPayload function is available
|
|
89
|
+
if (typeof generateRSCPayload !== 'function') {
|
|
90
|
+
throw new Error('generateRSCPayload is not defined. Please ensure that you are using at least version 4.0.0 of ' +
|
|
91
|
+
'React on Rails Pro and the Node renderer, and that ReactOnRailsPro.configuration.enable_rsc_support ' +
|
|
92
|
+
'is set to true.');
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const stream = await generateRSCPayload(componentName, props, this.railsContext);
|
|
96
|
+
// Tee stream to allow for multiple consumers:
|
|
97
|
+
// 1. stream1 - Used by React's runtime to perform server-side rendering
|
|
98
|
+
// 2. stream2 - Used by react-on-rails to embed the RSC payloads
|
|
99
|
+
// into the HTML stream for client-side hydration
|
|
100
|
+
const stream1 = new PassThrough();
|
|
101
|
+
stream.pipe(stream1);
|
|
102
|
+
const stream2 = new PassThrough();
|
|
103
|
+
stream.pipe(stream2);
|
|
104
|
+
const streamInfo = {
|
|
105
|
+
componentName,
|
|
106
|
+
props,
|
|
107
|
+
stream: stream2,
|
|
108
|
+
};
|
|
109
|
+
this.streams.push(streamInfo);
|
|
110
|
+
// Notify callbacks about the new stream in a sync manner to maintain proper hydration timing
|
|
111
|
+
this.callbacks.forEach((callback) => callback(streamInfo));
|
|
112
|
+
return stream1;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// Provide a more helpful error message that includes context
|
|
116
|
+
throw new Error(`Failed to generate RSC payload for component "${componentName}": ${extractErrorMessage(error)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Returns all RSC payload streams tracked by this request tracker.
|
|
121
|
+
* Used by the server renderer to access all fetched RSCs for this request.
|
|
122
|
+
*
|
|
123
|
+
* @returns Array of RSC payload stream information
|
|
124
|
+
*/
|
|
125
|
+
getRSCPayloadStreams() {
|
|
126
|
+
return [...this.streams]; // Return a copy to prevent external mutation
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export default RSCRequestTracker;
|
|
130
|
+
//# sourceMappingURL=RSCRequestTracker.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Renders a React Server Component inside a React Client Component.
|
|
4
|
+
*
|
|
5
|
+
* RSCRoute provides a bridge between client and server components, allowing server components
|
|
6
|
+
* to be directly rendered inside client components. This component:
|
|
7
|
+
*
|
|
8
|
+
* 1. During initial SSR - Uses the RSC payload to render the server component and embeds the payload in the page
|
|
9
|
+
* 2. During hydration - Uses the embedded RSC payload already in the page
|
|
10
|
+
* 3. During client navigation - Fetches the RSC payload via HTTP
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <RSCRoute componentName="MyServerComponent" componentProps={{ user }} />
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @important Only use for server components whose props change rarely. Frequent prop changes
|
|
18
|
+
* will cause network requests for each change, impacting performance.
|
|
19
|
+
*
|
|
20
|
+
* @important This component expects that the component tree that contains it is wrapped using
|
|
21
|
+
* wrapServerComponentRenderer from 'react-on-rails/wrapServerComponentRenderer/client' for client-side
|
|
22
|
+
* rendering or 'react-on-rails/wrapServerComponentRenderer/server' for server-side rendering.
|
|
23
|
+
*/
|
|
24
|
+
export type RSCRouteProps = {
|
|
25
|
+
componentName: string;
|
|
26
|
+
componentProps: unknown;
|
|
27
|
+
};
|
|
28
|
+
declare const RSCRoute: ({ componentName, componentProps }: RSCRouteProps) => ReactNode;
|
|
29
|
+
export default RSCRoute;
|
|
30
|
+
//# sourceMappingURL=RSCRoute.d.ts.map
|
package/lib/RSCRoute.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Shakacode LLC
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT licensed under the MIT (open source) license.
|
|
5
|
+
* It is part of the React on Rails Pro offering and is licensed separately.
|
|
6
|
+
*
|
|
7
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
8
|
+
* via any medium, is strictly prohibited without a valid license agreement
|
|
9
|
+
* from Shakacode LLC.
|
|
10
|
+
*
|
|
11
|
+
* For licensing terms, please see:
|
|
12
|
+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
|
+
*/
|
|
14
|
+
/// <reference types="react/experimental" />
|
|
15
|
+
'use client';
|
|
16
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
17
|
+
import { Component, use } from 'react';
|
|
18
|
+
import { useRSC } from "./RSCProvider.js";
|
|
19
|
+
import { ServerComponentFetchError } from "./ServerComponentFetchError.js";
|
|
20
|
+
/**
|
|
21
|
+
* Error boundary component for RSCRoute that adds server component name and props to the error
|
|
22
|
+
* So, the parent ErrorBoundary can refetch the server component
|
|
23
|
+
*/
|
|
24
|
+
class RSCRouteErrorBoundary extends Component {
|
|
25
|
+
constructor(props) {
|
|
26
|
+
super(props);
|
|
27
|
+
this.state = { error: null };
|
|
28
|
+
}
|
|
29
|
+
static getDerivedStateFromError(error) {
|
|
30
|
+
return { error };
|
|
31
|
+
}
|
|
32
|
+
render() {
|
|
33
|
+
const { error } = this.state;
|
|
34
|
+
const { componentName, componentProps, children } = this.props;
|
|
35
|
+
if (error) {
|
|
36
|
+
throw new ServerComponentFetchError(error.message, componentName, componentProps, error);
|
|
37
|
+
}
|
|
38
|
+
return children;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const PromiseWrapper = ({ promise }) => {
|
|
42
|
+
// use is available in React 18.3+
|
|
43
|
+
const promiseResult = use(promise);
|
|
44
|
+
// In case that an error happened during the rendering of the RSC payload before the rendering of the component itself starts
|
|
45
|
+
// RSC bundle will return an error object serialized inside the RSC payload
|
|
46
|
+
if (promiseResult instanceof Error) {
|
|
47
|
+
throw promiseResult;
|
|
48
|
+
}
|
|
49
|
+
return promiseResult;
|
|
50
|
+
};
|
|
51
|
+
const RSCRoute = ({ componentName, componentProps }) => {
|
|
52
|
+
const { getComponent } = useRSC();
|
|
53
|
+
const componentPromise = getComponent(componentName, componentProps);
|
|
54
|
+
return (_jsx(RSCRouteErrorBoundary, { componentName: componentName, componentProps: componentProps, children: _jsx(PromiseWrapper, { promise: componentPromise }) }));
|
|
55
|
+
};
|
|
56
|
+
export default RSCRoute;
|
|
57
|
+
//# sourceMappingURL=RSCRoute.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Shakacode LLC
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT licensed under the MIT (open source) license.
|
|
5
|
+
* It is part of the React on Rails Pro offering and is licensed separately.
|
|
6
|
+
*
|
|
7
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
8
|
+
* via any medium, is strictly prohibited without a valid license agreement
|
|
9
|
+
* from Shakacode LLC.
|
|
10
|
+
*
|
|
11
|
+
* For licensing terms, please see:
|
|
12
|
+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
|
+
*/
|
|
14
|
+
import { createBaseClientObject } from 'react-on-rails/@internal/base/client';
|
|
15
|
+
import createReactOnRailsPro from "./createReactOnRailsPro.js";
|
|
16
|
+
const currentGlobal = globalThis.ReactOnRails || null;
|
|
17
|
+
const ReactOnRails = createReactOnRailsPro(createBaseClientObject, currentGlobal);
|
|
18
|
+
export * from 'react-on-rails/types';
|
|
19
|
+
export default ReactOnRails;
|
|
20
|
+
//# sourceMappingURL=ReactOnRails.client.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Shakacode LLC
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT licensed under the MIT (open source) license.
|
|
5
|
+
* It is part of the React on Rails Pro offering and is licensed separately.
|
|
6
|
+
*
|
|
7
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
8
|
+
* via any medium, is strictly prohibited without a valid license agreement
|
|
9
|
+
* from Shakacode LLC.
|
|
10
|
+
*
|
|
11
|
+
* For licensing terms, please see:
|
|
12
|
+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
|
+
*/
|
|
14
|
+
import { createBaseFullObject } from 'react-on-rails/@internal/base/full';
|
|
15
|
+
import createReactOnRailsPro from "./createReactOnRailsPro.js";
|
|
16
|
+
// Warn about bundle size when included in browser bundles
|
|
17
|
+
if (typeof window !== 'undefined') {
|
|
18
|
+
console.warn('Optimization opportunity: "react-on-rails-pro" includes ~14KB of server-rendering code. ' +
|
|
19
|
+
'Browsers may not need it. See https://forum.shakacode.com/t/how-to-use-different-versions-of-a-file-for-client-and-server-rendering/1352 ' +
|
|
20
|
+
'(Requires creating a free account). Click this for the stack trace.');
|
|
21
|
+
}
|
|
22
|
+
const currentGlobal = globalThis.ReactOnRails || null;
|
|
23
|
+
const ReactOnRails = createReactOnRailsPro(createBaseFullObject, currentGlobal);
|
|
24
|
+
export * from 'react-on-rails/types';
|
|
25
|
+
export default ReactOnRails;
|
|
26
|
+
//# sourceMappingURL=ReactOnRails.full.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Shakacode LLC
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT licensed under the MIT (open source) license.
|
|
5
|
+
* It is part of the React on Rails Pro offering and is licensed separately.
|
|
6
|
+
*
|
|
7
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
8
|
+
* via any medium, is strictly prohibited without a valid license agreement
|
|
9
|
+
* from Shakacode LLC.
|
|
10
|
+
*
|
|
11
|
+
* For licensing terms, please see:
|
|
12
|
+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
|
+
*/
|
|
14
|
+
import ReactOnRails from "./ReactOnRails.full.js";
|
|
15
|
+
import streamServerRenderedReactComponent from "./streamServerRenderedReactComponent.js";
|
|
16
|
+
// Add Pro server-side streaming functionality
|
|
17
|
+
ReactOnRails.streamServerRenderedReactComponent = streamServerRenderedReactComponent;
|
|
18
|
+
export * from "./ReactOnRails.full.js";
|
|
19
|
+
// eslint-disable-next-line no-restricted-exports -- see https://github.com/eslint/eslint/issues/15617
|
|
20
|
+
export { default } from "./ReactOnRails.full.js";
|
|
21
|
+
//# sourceMappingURL=ReactOnRails.node.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Shakacode LLC
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT licensed under the MIT (open source) license.
|
|
5
|
+
* It is part of the React on Rails Pro offering and is licensed separately.
|
|
6
|
+
*
|
|
7
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
8
|
+
* via any medium, is strictly prohibited without a valid license agreement
|
|
9
|
+
* from Shakacode LLC.
|
|
10
|
+
*
|
|
11
|
+
* For licensing terms, please see:
|
|
12
|
+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
|
+
*/
|
|
14
|
+
import { buildServerRenderer } from 'react-on-rails-rsc/server.node';
|
|
15
|
+
import { assertRailsContextWithServerStreamingCapabilities, } from 'react-on-rails/types';
|
|
16
|
+
import { convertToError } from 'react-on-rails/serverRenderUtils';
|
|
17
|
+
import handleError from "./handleErrorRSC.js";
|
|
18
|
+
import ReactOnRails from "./ReactOnRails.full.js";
|
|
19
|
+
import { streamServerRenderedComponent, transformRenderStreamChunksToResultObject, } from "./streamingUtils.js";
|
|
20
|
+
import loadJsonFile from "./loadJsonFile.js";
|
|
21
|
+
let serverRendererPromise;
|
|
22
|
+
const streamRenderRSCComponent = (reactRenderingResult, options, streamingTrackers) => {
|
|
23
|
+
const { throwJsErrors } = options;
|
|
24
|
+
const { railsContext } = options;
|
|
25
|
+
assertRailsContextWithServerStreamingCapabilities(railsContext);
|
|
26
|
+
const { reactClientManifestFileName } = railsContext;
|
|
27
|
+
const renderState = {
|
|
28
|
+
result: null,
|
|
29
|
+
hasErrors: false,
|
|
30
|
+
isShellReady: true,
|
|
31
|
+
};
|
|
32
|
+
const { pipeToTransform, readableStream, emitError } = transformRenderStreamChunksToResultObject(renderState);
|
|
33
|
+
const reportError = (error) => {
|
|
34
|
+
console.error('Error in RSC stream', error);
|
|
35
|
+
if (throwJsErrors) {
|
|
36
|
+
emitError(error);
|
|
37
|
+
}
|
|
38
|
+
renderState.hasErrors = true;
|
|
39
|
+
renderState.error = error;
|
|
40
|
+
};
|
|
41
|
+
const initializeAndRender = async () => {
|
|
42
|
+
if (!serverRendererPromise) {
|
|
43
|
+
serverRendererPromise = loadJsonFile(reactClientManifestFileName)
|
|
44
|
+
.then((reactClientManifest) => buildServerRenderer(reactClientManifest))
|
|
45
|
+
.catch((err) => {
|
|
46
|
+
serverRendererPromise = undefined;
|
|
47
|
+
throw err;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const { renderToPipeableStream } = await serverRendererPromise;
|
|
51
|
+
const rscStream = renderToPipeableStream(await reactRenderingResult, {
|
|
52
|
+
onError: (err) => {
|
|
53
|
+
const error = convertToError(err);
|
|
54
|
+
reportError(error);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
pipeToTransform(rscStream);
|
|
58
|
+
};
|
|
59
|
+
initializeAndRender().catch((e) => {
|
|
60
|
+
const error = convertToError(e);
|
|
61
|
+
reportError(error);
|
|
62
|
+
const errorHtml = handleError({ e: error, name: options.name, serverSide: true });
|
|
63
|
+
pipeToTransform(errorHtml);
|
|
64
|
+
});
|
|
65
|
+
readableStream.on('end', () => {
|
|
66
|
+
streamingTrackers.postSSRHookTracker.notifySSREnd();
|
|
67
|
+
});
|
|
68
|
+
return readableStream;
|
|
69
|
+
};
|
|
70
|
+
ReactOnRails.serverRenderRSCReactComponent = (options) => {
|
|
71
|
+
try {
|
|
72
|
+
return streamServerRenderedComponent(options, streamRenderRSCComponent, handleError);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
console.history = [];
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
ReactOnRails.isRSCBundle = true;
|
|
79
|
+
export * from 'react-on-rails/types';
|
|
80
|
+
export default ReactOnRails;
|
|
81
|
+
//# sourceMappingURL=ReactOnRailsRSC.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error type for when there's an issue fetching or rendering a server component.
|
|
3
|
+
* This error includes information about the server component and the original error that occurred.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ServerComponentFetchError extends Error {
|
|
6
|
+
serverComponentName: string;
|
|
7
|
+
serverComponentProps: unknown;
|
|
8
|
+
originalError: Error;
|
|
9
|
+
constructor(message: string, componentName: string, componentProps: unknown, originalError: Error);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Type guard to check if an error is a ServerComponentFetchError
|
|
13
|
+
*/
|
|
14
|
+
export declare function isServerComponentFetchError(error: unknown): error is ServerComponentFetchError;
|
|
15
|
+
//# sourceMappingURL=ServerComponentFetchError.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Shakacode LLC
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT licensed under the MIT (open source) license.
|
|
5
|
+
* It is part of the React on Rails Pro offering and is licensed separately.
|
|
6
|
+
*
|
|
7
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
8
|
+
* via any medium, is strictly prohibited without a valid license agreement
|
|
9
|
+
* from Shakacode LLC.
|
|
10
|
+
*
|
|
11
|
+
* For licensing terms, please see:
|
|
12
|
+
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Custom error type for when there's an issue fetching or rendering a server component.
|
|
16
|
+
* This error includes information about the server component and the original error that occurred.
|
|
17
|
+
*/
|
|
18
|
+
export class ServerComponentFetchError extends Error {
|
|
19
|
+
constructor(message, componentName, componentProps, originalError) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'ServerComponentFetchError';
|
|
22
|
+
this.serverComponentName = componentName;
|
|
23
|
+
this.serverComponentProps = componentProps;
|
|
24
|
+
this.originalError = originalError;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Type guard to check if an error is a ServerComponentFetchError
|
|
29
|
+
*/
|
|
30
|
+
export function isServerComponentFetchError(error) {
|
|
31
|
+
return error instanceof ServerComponentFetchError;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=ServerComponentFetchError.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Store, StoreGenerator } from 'react-on-rails/types';
|
|
2
|
+
/**
|
|
3
|
+
* Register a store generator, a function that takes props and returns a store.
|
|
4
|
+
* @param storeGenerators { name1: storeGenerator1, name2: storeGenerator2 }
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export declare function register(storeGenerators: Record<string, StoreGenerator>): void;
|
|
8
|
+
/**
|
|
9
|
+
* Used by components to get the hydrated store which contains props.
|
|
10
|
+
* @param name
|
|
11
|
+
* @param throwIfMissing Defaults to true. Set to false to have this call return undefined if
|
|
12
|
+
* there is no store with the given name.
|
|
13
|
+
* @returns Redux Store, possibly hydrated
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export declare function getStore(name: string, throwIfMissing?: boolean): Store | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Internally used function to get the store creator that was passed to `register`.
|
|
19
|
+
* @param name
|
|
20
|
+
* @returns storeCreator with given name
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
export declare const getStoreGenerator: (name: string) => StoreGenerator;
|
|
24
|
+
/**
|
|
25
|
+
* Internally used function to set the hydrated store after a Rails page is loaded.
|
|
26
|
+
* @param name
|
|
27
|
+
* @param store (not the storeGenerator, but the hydrated store)
|
|
28
|
+
*/
|
|
29
|
+
export declare function setStore(name: string, store: Store): void;
|
|
30
|
+
/**
|
|
31
|
+
* Internally used function to completely clear hydratedStores Map.
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
export declare function clearHydratedStores(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Get a Map containing all registered store generators. Useful for debugging.
|
|
37
|
+
* @returns Map where key is the component name and values are the store generators.
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
export declare const storeGenerators: () => Map<string, StoreGenerator>;
|
|
41
|
+
/**
|
|
42
|
+
* Get a Map containing all hydrated stores. Useful for debugging.
|
|
43
|
+
* @returns Map where key is the component name and values are the hydrated stores.
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export declare const stores: () => Map<string, Store>;
|
|
47
|
+
/**
|
|
48
|
+
* Used by components to get the hydrated store, waiting for it to be hydrated if necessary.
|
|
49
|
+
* @param name Name of the store to wait for
|
|
50
|
+
* @returns Promise that resolves with the Store once hydrated
|
|
51
|
+
*/
|
|
52
|
+
export declare const getOrWaitForStore: (name: string) => Promise<Store>;
|
|
53
|
+
/**
|
|
54
|
+
* Used by components to get the store generator, waiting for it to be registered if necessary.
|
|
55
|
+
* @param name Name of the store generator to wait for
|
|
56
|
+
* @returns Promise that resolves with the StoreGenerator once registered
|
|
57
|
+
*/
|
|
58
|
+
export declare const getOrWaitForStoreGenerator: (name: string) => Promise<StoreGenerator>;
|
|
59
|
+
//# sourceMappingURL=StoreRegistry.d.ts.map
|