react-on-rails-pro 16.6.0 → 16.7.0-rc.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/README.md +5 -2
- package/lib/AsyncPropsManager.d.ts +61 -0
- package/lib/AsyncPropsManager.js +134 -0
- package/lib/RSCRequestTracker.d.ts +3 -14
- package/lib/RSCRequestTracker.js +8 -7
- package/lib/ReactOnRails.client.js +1 -2
- package/lib/ReactOnRails.full.js +2 -8
- package/lib/ReactOnRails.node.d.ts +3 -2
- package/lib/ReactOnRails.node.js +7 -7
- package/lib/ReactOnRailsRSC.d.ts +1 -1
- package/lib/ReactOnRailsRSC.js +5 -65
- package/lib/capabilities/proLifecycle.d.ts +12 -0
- package/lib/capabilities/proLifecycle.js +34 -0
- package/lib/capabilities/proMethods.d.ts +12 -0
- package/lib/capabilities/proMethods.js +25 -0
- package/lib/capabilities/proRSC.d.ts +17 -0
- package/lib/capabilities/proRSC.js +88 -0
- package/lib/capabilities/proStreaming.d.ts +10 -0
- package/lib/capabilities/proStreaming.js +14 -0
- package/lib/createReactOnRailsPro.d.ts +7 -5
- package/lib/createReactOnRailsPro.js +23 -103
- package/lib/getReactServerComponent.client.js +68 -13
- package/lib/injectRSCPayload.js +108 -26
- package/lib/parseLengthPrefixedStream.d.ts +9 -0
- package/lib/parseLengthPrefixedStream.js +96 -0
- package/lib/proClientStartup.d.ts +2 -0
- package/lib/proClientStartup.js +51 -0
- package/lib/streamingUtils.js +16 -11
- package/lib/transformRSCNodeStream.js +6 -15
- package/lib/utils.d.ts +0 -1
- package/lib/utils.js +0 -4
- package/package.json +5 -4
- package/lib/transformRSCStreamAndReplayConsoleLogs.d.ts +0 -16
- package/lib/transformRSCStreamAndReplayConsoleLogs.js +0 -86
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# react-on-rails-pro
|
|
2
2
|
|
|
3
|
-
The client-side JavaScript package for [React on Rails Pro](https://
|
|
3
|
+
The client-side JavaScript package for [React on Rails Pro](https://reactonrails.com/docs/pro/). This package **replaces** the base `react-on-rails` package and re-exports everything from it, plus Pro-exclusive features like React Server Components support.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -14,6 +14,8 @@ pnpm add react-on-rails-pro
|
|
|
14
14
|
|
|
15
15
|
**Important:** When using the `react_on_rails_pro` Ruby gem, you **must** use this package (`react-on-rails-pro`) instead of `react-on-rails`. If the Pro gem detects the base `react-on-rails` npm package at runtime, it will raise an error.
|
|
16
16
|
|
|
17
|
+
React on Rails Pro is free to evaluate in development, CI/CD, and staging. A paid license is required only for production deployments.
|
|
18
|
+
|
|
17
19
|
## Usage
|
|
18
20
|
|
|
19
21
|
### Component Registration
|
|
@@ -90,7 +92,8 @@ See the [full installation guide](https://reactonrails.com/docs/pro/installation
|
|
|
90
92
|
- [Installation Guide](https://reactonrails.com/docs/pro/installation)
|
|
91
93
|
- [Configuration Reference](https://reactonrails.com/docs/configuration/configuration-pro)
|
|
92
94
|
- [React Server Components Tutorial](https://reactonrails.com/docs/pro/react-server-components/tutorial)
|
|
93
|
-
- [
|
|
95
|
+
- [Upgrade from OSS to Pro](https://reactonrails.com/docs/pro/upgrading-to-pro/)
|
|
96
|
+
- [React on Rails Pro Overview](https://reactonrails.com/docs/pro/)
|
|
94
97
|
|
|
95
98
|
## License
|
|
96
99
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages async props for incremental server-side rendering.
|
|
3
|
+
*
|
|
4
|
+
* DESIGN PRINCIPLES:
|
|
5
|
+
*
|
|
6
|
+
* 1. PROMISE CACHING: Same promise is returned for multiple getProp() calls.
|
|
7
|
+
* This is CRITICAL for React's rendering model - if we returned new promises,
|
|
8
|
+
* React would create infinite render loops or flicker as each render would
|
|
9
|
+
* get a different promise object.
|
|
10
|
+
*
|
|
11
|
+
* 2. ORDER INDEPENDENCE: Props can be set before or after they're requested.
|
|
12
|
+
* - If getProp() is called first: Creates promise, suspends, later setProp() resolves it
|
|
13
|
+
* - If setProp() is called first: Creates resolved promise, getProp() returns immediately
|
|
14
|
+
*
|
|
15
|
+
* 3. STREAM LIFECYCLE: endStream() rejects all unresolved props.
|
|
16
|
+
* This handles the case where the HTTP request closes before all props arrive,
|
|
17
|
+
* allowing React to show error boundaries instead of hanging forever.
|
|
18
|
+
*
|
|
19
|
+
* USAGE FLOW:
|
|
20
|
+
* 1. ServerRenderingJsCode calls addAsyncPropsCapabilityToComponentProps()
|
|
21
|
+
* 2. Component calls getReactOnRailsAsyncProp("propName") → getProp() returns promise
|
|
22
|
+
* 3. React suspends on the promise
|
|
23
|
+
* 4. Rails sends update chunk → setProp("propName", value) → promise resolves
|
|
24
|
+
* 5. React resumes rendering with the value
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Inside a React Server Component
|
|
28
|
+
* async function MyComponent({ getReactOnRailsAsyncProp }) {
|
|
29
|
+
* const users = await getReactOnRailsAsyncProp('users');
|
|
30
|
+
* return <UserList users={users} />;
|
|
31
|
+
* }
|
|
32
|
+
*/
|
|
33
|
+
declare class AsyncPropsManager {
|
|
34
|
+
private isClosed;
|
|
35
|
+
private propNameToPromiseController;
|
|
36
|
+
/**
|
|
37
|
+
* Gets the promise for an async prop. Returns the SAME promise on repeated calls.
|
|
38
|
+
*
|
|
39
|
+
* IMPORTANT: This is not an async function intentionally.
|
|
40
|
+
* Returning the same Promise object on every call is required for React's
|
|
41
|
+
* concurrent rendering - new promises would cause re-renders.
|
|
42
|
+
*/
|
|
43
|
+
getProp(propName: string): Promise<unknown>;
|
|
44
|
+
setProp(propName: string, propValue: unknown): void;
|
|
45
|
+
endStream(): void;
|
|
46
|
+
private getOrCreatePromiseController;
|
|
47
|
+
private static getNoPropFoundError;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Gets or creates an AsyncPropsManager from the shared execution context.
|
|
51
|
+
*
|
|
52
|
+
* This function implements lazy initialization to handle race conditions between
|
|
53
|
+
* the initial render request and update chunks. Whichever executes first will
|
|
54
|
+
* create the manager, and subsequent calls will reuse the same instance.
|
|
55
|
+
*
|
|
56
|
+
* @param sharedExecutionContext - Map scoped to the current HTTP request
|
|
57
|
+
* @returns The AsyncPropsManager instance (existing or newly created)
|
|
58
|
+
*/
|
|
59
|
+
export declare function getOrCreateAsyncPropsManager(sharedExecutionContext: Map<string, unknown>): AsyncPropsManager;
|
|
60
|
+
export default AsyncPropsManager;
|
|
61
|
+
//# sourceMappingURL=AsyncPropsManager.d.ts.map
|
|
@@ -0,0 +1,134 @@
|
|
|
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
|
+
* Manages async props for incremental server-side rendering.
|
|
16
|
+
*
|
|
17
|
+
* DESIGN PRINCIPLES:
|
|
18
|
+
*
|
|
19
|
+
* 1. PROMISE CACHING: Same promise is returned for multiple getProp() calls.
|
|
20
|
+
* This is CRITICAL for React's rendering model - if we returned new promises,
|
|
21
|
+
* React would create infinite render loops or flicker as each render would
|
|
22
|
+
* get a different promise object.
|
|
23
|
+
*
|
|
24
|
+
* 2. ORDER INDEPENDENCE: Props can be set before or after they're requested.
|
|
25
|
+
* - If getProp() is called first: Creates promise, suspends, later setProp() resolves it
|
|
26
|
+
* - If setProp() is called first: Creates resolved promise, getProp() returns immediately
|
|
27
|
+
*
|
|
28
|
+
* 3. STREAM LIFECYCLE: endStream() rejects all unresolved props.
|
|
29
|
+
* This handles the case where the HTTP request closes before all props arrive,
|
|
30
|
+
* allowing React to show error boundaries instead of hanging forever.
|
|
31
|
+
*
|
|
32
|
+
* USAGE FLOW:
|
|
33
|
+
* 1. ServerRenderingJsCode calls addAsyncPropsCapabilityToComponentProps()
|
|
34
|
+
* 2. Component calls getReactOnRailsAsyncProp("propName") → getProp() returns promise
|
|
35
|
+
* 3. React suspends on the promise
|
|
36
|
+
* 4. Rails sends update chunk → setProp("propName", value) → promise resolves
|
|
37
|
+
* 5. React resumes rendering with the value
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Inside a React Server Component
|
|
41
|
+
* async function MyComponent({ getReactOnRailsAsyncProp }) {
|
|
42
|
+
* const users = await getReactOnRailsAsyncProp('users');
|
|
43
|
+
* return <UserList users={users} />;
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
class AsyncPropsManager {
|
|
47
|
+
constructor() {
|
|
48
|
+
this.isClosed = false;
|
|
49
|
+
this.propNameToPromiseController = new Map();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Gets the promise for an async prop. Returns the SAME promise on repeated calls.
|
|
53
|
+
*
|
|
54
|
+
* IMPORTANT: This is not an async function intentionally.
|
|
55
|
+
* Returning the same Promise object on every call is required for React's
|
|
56
|
+
* concurrent rendering - new promises would cause re-renders.
|
|
57
|
+
*/
|
|
58
|
+
getProp(propName) {
|
|
59
|
+
const promiseController = this.getOrCreatePromiseController(propName);
|
|
60
|
+
if (!promiseController) {
|
|
61
|
+
return Promise.reject(AsyncPropsManager.getNoPropFoundError(propName));
|
|
62
|
+
}
|
|
63
|
+
return promiseController.promise;
|
|
64
|
+
}
|
|
65
|
+
setProp(propName, propValue) {
|
|
66
|
+
const promiseController = this.getOrCreatePromiseController(propName);
|
|
67
|
+
if (!promiseController) {
|
|
68
|
+
throw new Error(`Can't set the async prop "${propName}" because the stream is already closed`);
|
|
69
|
+
}
|
|
70
|
+
promiseController.resolve(propValue);
|
|
71
|
+
promiseController.resolved = true;
|
|
72
|
+
}
|
|
73
|
+
endStream() {
|
|
74
|
+
if (this.isClosed) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
this.isClosed = true;
|
|
78
|
+
this.propNameToPromiseController.forEach((promiseController, propName) => {
|
|
79
|
+
if (!promiseController.resolved) {
|
|
80
|
+
promiseController.reject(AsyncPropsManager.getNoPropFoundError(propName));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
getOrCreatePromiseController(propName) {
|
|
85
|
+
const promiseController = this.propNameToPromiseController.get(propName);
|
|
86
|
+
if (promiseController) {
|
|
87
|
+
return promiseController;
|
|
88
|
+
}
|
|
89
|
+
if (this.isClosed) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const partialPromiseController = {
|
|
93
|
+
resolved: false,
|
|
94
|
+
};
|
|
95
|
+
let resolvePromise = () => { };
|
|
96
|
+
let rejectPromise = () => { };
|
|
97
|
+
const promise = new Promise((resolve, reject) => {
|
|
98
|
+
resolvePromise = resolve;
|
|
99
|
+
rejectPromise = reject;
|
|
100
|
+
});
|
|
101
|
+
const newPromiseController = Object.assign(partialPromiseController, {
|
|
102
|
+
promise,
|
|
103
|
+
resolve: resolvePromise,
|
|
104
|
+
reject: rejectPromise,
|
|
105
|
+
});
|
|
106
|
+
this.propNameToPromiseController.set(propName, newPromiseController);
|
|
107
|
+
return newPromiseController;
|
|
108
|
+
}
|
|
109
|
+
static getNoPropFoundError(propName) {
|
|
110
|
+
return new Error(`The async prop "${propName}" is not received. Ensure to send the async prop from ruby side`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const ASYNC_PROPS_MANAGER_KEY = 'asyncPropsManager';
|
|
114
|
+
/**
|
|
115
|
+
* Gets or creates an AsyncPropsManager from the shared execution context.
|
|
116
|
+
*
|
|
117
|
+
* This function implements lazy initialization to handle race conditions between
|
|
118
|
+
* the initial render request and update chunks. Whichever executes first will
|
|
119
|
+
* create the manager, and subsequent calls will reuse the same instance.
|
|
120
|
+
*
|
|
121
|
+
* @param sharedExecutionContext - Map scoped to the current HTTP request
|
|
122
|
+
* @returns The AsyncPropsManager instance (existing or newly created)
|
|
123
|
+
*/
|
|
124
|
+
export function getOrCreateAsyncPropsManager(sharedExecutionContext) {
|
|
125
|
+
let manager = sharedExecutionContext.get(ASYNC_PROPS_MANAGER_KEY);
|
|
126
|
+
if (manager) {
|
|
127
|
+
return manager;
|
|
128
|
+
}
|
|
129
|
+
manager = new AsyncPropsManager();
|
|
130
|
+
sharedExecutionContext.set(ASYNC_PROPS_MANAGER_KEY, manager);
|
|
131
|
+
return manager;
|
|
132
|
+
}
|
|
133
|
+
export default AsyncPropsManager;
|
|
134
|
+
//# sourceMappingURL=AsyncPropsManager.js.map
|
|
@@ -1,16 +1,4 @@
|
|
|
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
|
-
}
|
|
1
|
+
import { RSCPayloadStreamInfo, RSCPayloadCallback, RailsContextWithServerComponentMetadata, GenerateRSCPayloadFunction } from 'react-on-rails/types';
|
|
14
2
|
/**
|
|
15
3
|
* RSC Request Tracker - manages RSC payload generation and tracking for a single request.
|
|
16
4
|
*
|
|
@@ -23,7 +11,8 @@ declare class RSCRequestTracker {
|
|
|
23
11
|
private streams;
|
|
24
12
|
private callbacks;
|
|
25
13
|
private railsContext;
|
|
26
|
-
|
|
14
|
+
private generateRSCPayload?;
|
|
15
|
+
constructor(railsContext: RailsContextWithServerComponentMetadata, generateRSCPayload?: GenerateRSCPayloadFunction);
|
|
27
16
|
/**
|
|
28
17
|
* Clears all streams and callbacks for this request.
|
|
29
18
|
* Should be called when the request is complete to ensure proper cleanup,
|
package/lib/RSCRequestTracker.js
CHANGED
|
@@ -22,10 +22,11 @@ import { extractErrorMessage } from "./utils.js";
|
|
|
22
22
|
* functionality for components.
|
|
23
23
|
*/
|
|
24
24
|
class RSCRequestTracker {
|
|
25
|
-
constructor(railsContext) {
|
|
25
|
+
constructor(railsContext, generateRSCPayload) {
|
|
26
26
|
this.streams = [];
|
|
27
27
|
this.callbacks = [];
|
|
28
28
|
this.railsContext = railsContext;
|
|
29
|
+
this.generateRSCPayload = generateRSCPayload;
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
31
32
|
* Clears all streams and callbacks for this request.
|
|
@@ -85,14 +86,14 @@ class RSCRequestTracker {
|
|
|
85
86
|
* @throws Error if generateRSCPayload is not available or fails
|
|
86
87
|
*/
|
|
87
88
|
async getRSCPayloadStream(componentName, props) {
|
|
88
|
-
// Validate that the
|
|
89
|
-
if (
|
|
90
|
-
throw new Error('generateRSCPayload is not
|
|
91
|
-
'
|
|
92
|
-
'
|
|
89
|
+
// Validate that the generateRSCPayload function is available
|
|
90
|
+
if (!this.generateRSCPayload) {
|
|
91
|
+
throw new Error('generateRSCPayload function is not available. This could mean: ' +
|
|
92
|
+
'(1) ReactOnRailsPro.configuration.enable_rsc_support is not enabled, or ' +
|
|
93
|
+
'(2) You are using an incompatible version of React on Rails Pro (requires 4.0.0+).');
|
|
93
94
|
}
|
|
94
95
|
try {
|
|
95
|
-
const stream = await generateRSCPayload(componentName, props, this.railsContext);
|
|
96
|
+
const stream = await this.generateRSCPayload(componentName, props, this.railsContext);
|
|
96
97
|
// Tee stream to allow for multiple consumers:
|
|
97
98
|
// 1. stream1 - Used by React's runtime to perform server-side rendering
|
|
98
99
|
// 2. stream2 - Used by react-on-rails to embed the RSC payloads
|
|
@@ -11,10 +11,9 @@
|
|
|
11
11
|
* For licensing terms, please see:
|
|
12
12
|
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
13
|
*/
|
|
14
|
-
import { createBaseClientObject } from 'react-on-rails/@internal/base/client';
|
|
15
14
|
import createReactOnRailsPro from "./createReactOnRailsPro.js";
|
|
16
15
|
const currentGlobal = globalThis.ReactOnRails || null;
|
|
17
|
-
const ReactOnRails = createReactOnRailsPro(
|
|
16
|
+
const ReactOnRails = createReactOnRailsPro([], currentGlobal);
|
|
18
17
|
export * from 'react-on-rails/types';
|
|
19
18
|
export default ReactOnRails;
|
|
20
19
|
//# sourceMappingURL=ReactOnRails.client.js.map
|
package/lib/ReactOnRails.full.js
CHANGED
|
@@ -11,16 +11,10 @@
|
|
|
11
11
|
* For licensing terms, please see:
|
|
12
12
|
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { createSSRCapability } from 'react-on-rails/@internal/capabilities/ssr';
|
|
15
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
16
|
const currentGlobal = globalThis.ReactOnRails || null;
|
|
23
|
-
const ReactOnRails = createReactOnRailsPro(
|
|
17
|
+
const ReactOnRails = createReactOnRailsPro([createSSRCapability()], currentGlobal);
|
|
24
18
|
export * from 'react-on-rails/types';
|
|
25
19
|
export default ReactOnRails;
|
|
26
20
|
//# sourceMappingURL=ReactOnRails.full.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
declare const ReactOnRails: import("react-on-rails/types").ReactOnRailsInternal;
|
|
2
|
+
export * from 'react-on-rails/types';
|
|
3
|
+
export default ReactOnRails;
|
|
3
4
|
//# sourceMappingURL=ReactOnRails.node.d.ts.map
|
package/lib/ReactOnRails.node.js
CHANGED
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
* For licensing terms, please see:
|
|
12
12
|
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
13
|
*/
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export
|
|
14
|
+
import { createSSRCapability } from 'react-on-rails/@internal/capabilities/ssr';
|
|
15
|
+
import { createProStreamingCapability } from "./capabilities/proStreaming.js";
|
|
16
|
+
import createReactOnRailsPro from "./createReactOnRailsPro.js";
|
|
17
|
+
const currentGlobal = globalThis.ReactOnRails || null;
|
|
18
|
+
const ReactOnRails = createReactOnRailsPro([createSSRCapability(), createProStreamingCapability()], currentGlobal);
|
|
19
|
+
export * from 'react-on-rails/types';
|
|
20
|
+
export default ReactOnRails;
|
|
21
21
|
//# sourceMappingURL=ReactOnRails.node.js.map
|
package/lib/ReactOnRailsRSC.d.ts
CHANGED
package/lib/ReactOnRailsRSC.js
CHANGED
|
@@ -11,71 +11,11 @@
|
|
|
11
11
|
* For licensing terms, please see:
|
|
12
12
|
* https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
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;
|
|
14
|
+
import { createSSRCapability } from 'react-on-rails/@internal/capabilities/ssr';
|
|
15
|
+
import { createProRSCCapability } from "./capabilities/proRSC.js";
|
|
16
|
+
import createReactOnRailsPro from "./createReactOnRailsPro.js";
|
|
17
|
+
const currentGlobal = globalThis.ReactOnRails || null;
|
|
18
|
+
const ReactOnRails = createReactOnRailsPro([createSSRCapability(), createProRSCCapability()], currentGlobal);
|
|
79
19
|
export * from 'react-on-rails/types';
|
|
80
20
|
export default ReactOnRails;
|
|
81
21
|
//# sourceMappingURL=ReactOnRailsRSC.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Shared implementation used by both the capability object and proClientStartup. */
|
|
2
|
+
export declare function reactOnRailsPageLoaded(): Promise<void>;
|
|
3
|
+
/**
|
|
4
|
+
* Pro lifecycle capability.
|
|
5
|
+
* Overrides core lifecycle with Pro implementations that support hydration
|
|
6
|
+
* and on-demand rendering.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createProLifecycleCapability(): {
|
|
9
|
+
reactOnRailsPageLoaded: typeof reactOnRailsPageLoaded;
|
|
10
|
+
reactOnRailsComponentLoaded(domId: string): Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=proLifecycle.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
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 { debugTurbolinks } from 'react-on-rails/turbolinksUtils';
|
|
15
|
+
import { renderOrHydrateComponent, renderOrHydrateAllComponents, hydrateAllStores, } from "../ClientSideRenderer.js";
|
|
16
|
+
/** Shared implementation used by both the capability object and proClientStartup. */
|
|
17
|
+
export async function reactOnRailsPageLoaded() {
|
|
18
|
+
debugTurbolinks('reactOnRailsPageLoaded [PRO]');
|
|
19
|
+
await Promise.all([hydrateAllStores(), renderOrHydrateAllComponents()]);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Pro lifecycle capability.
|
|
23
|
+
* Overrides core lifecycle with Pro implementations that support hydration
|
|
24
|
+
* and on-demand rendering.
|
|
25
|
+
*/
|
|
26
|
+
export function createProLifecycleCapability() {
|
|
27
|
+
return {
|
|
28
|
+
reactOnRailsPageLoaded,
|
|
29
|
+
reactOnRailsComponentLoaded(domId) {
|
|
30
|
+
return renderOrHydrateComponent(domId);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=proLifecycle.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RegisteredComponent, Store, StoreGenerator } from 'react-on-rails/types';
|
|
2
|
+
/**
|
|
3
|
+
* Pro methods capability.
|
|
4
|
+
* Provides Pro-only features: async component/store access and store hydration.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createProMethodCapability(): {
|
|
7
|
+
getOrWaitForComponent(name: string): Promise<RegisteredComponent>;
|
|
8
|
+
getOrWaitForStore(name: string): Promise<Store>;
|
|
9
|
+
getOrWaitForStoreGenerator(name: string): Promise<StoreGenerator>;
|
|
10
|
+
reactOnRailsStoreLoaded(storeName: string): Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=proMethods.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export -- named export for consistency with capability API */
|
|
2
|
+
import * as ProComponentRegistry from "../ComponentRegistry.js";
|
|
3
|
+
import * as ProStoreRegistry from "../StoreRegistry.js";
|
|
4
|
+
import { hydrateStore } from "../ClientSideRenderer.js";
|
|
5
|
+
/**
|
|
6
|
+
* Pro methods capability.
|
|
7
|
+
* Provides Pro-only features: async component/store access and store hydration.
|
|
8
|
+
*/
|
|
9
|
+
export function createProMethodCapability() {
|
|
10
|
+
return {
|
|
11
|
+
getOrWaitForComponent(name) {
|
|
12
|
+
return ProComponentRegistry.getOrWaitForComponent(name);
|
|
13
|
+
},
|
|
14
|
+
getOrWaitForStore(name) {
|
|
15
|
+
return ProStoreRegistry.getOrWaitForStore(name);
|
|
16
|
+
},
|
|
17
|
+
getOrWaitForStoreGenerator(name) {
|
|
18
|
+
return ProStoreRegistry.getOrWaitForStoreGenerator(name);
|
|
19
|
+
},
|
|
20
|
+
reactOnRailsStoreLoaded(storeName) {
|
|
21
|
+
return hydrateStore(storeName);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=proMethods.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Readable } from 'stream';
|
|
2
|
+
import { RSCRenderParams } from 'react-on-rails/types';
|
|
3
|
+
/**
|
|
4
|
+
* Pro RSC capability.
|
|
5
|
+
* Provides React Server Components rendering support.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createProRSCCapability(): {
|
|
8
|
+
isRSCBundle: true;
|
|
9
|
+
serverRenderRSCReactComponent(options: RSCRenderParams): Readable;
|
|
10
|
+
addAsyncPropsCapabilityToComponentProps<AsyncPropsType extends Record<string, unknown>, PropsType extends Record<string, unknown>>(props: PropsType, sharedExecutionContext: Map<string, unknown>): {
|
|
11
|
+
props: PropsType & {
|
|
12
|
+
getReactOnRailsAsyncProp: <PropName extends keyof AsyncPropsType>(propName: PropName) => Promise<AsyncPropsType[PropName]>;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
getOrCreateAsyncPropsManager(sharedExecutionContext: Map<string, unknown>): import("../AsyncPropsManager.ts").default;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=proRSC.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export -- named export for consistency with capability API */
|
|
2
|
+
import { buildServerRenderer } from 'react-on-rails-rsc/server.node';
|
|
3
|
+
import { assertRailsContextWithServerStreamingCapabilities, } from 'react-on-rails/types';
|
|
4
|
+
import { convertToError } from 'react-on-rails/serverRenderUtils';
|
|
5
|
+
import handleError from "../handleErrorRSC.js";
|
|
6
|
+
import { getOrCreateAsyncPropsManager } from "../AsyncPropsManager.js";
|
|
7
|
+
import { streamServerRenderedComponent, transformRenderStreamChunksToResultObject, } from "../streamingUtils.js";
|
|
8
|
+
import loadJsonFile from "../loadJsonFile.js";
|
|
9
|
+
let serverRendererPromise;
|
|
10
|
+
const streamRenderRSCComponent = (reactRenderingResult, options, streamingTrackers) => {
|
|
11
|
+
const { throwJsErrors } = options;
|
|
12
|
+
const { railsContext } = options;
|
|
13
|
+
assertRailsContextWithServerStreamingCapabilities(railsContext);
|
|
14
|
+
const { reactClientManifestFileName } = railsContext;
|
|
15
|
+
const renderState = {
|
|
16
|
+
result: null,
|
|
17
|
+
hasErrors: false,
|
|
18
|
+
isShellReady: true,
|
|
19
|
+
};
|
|
20
|
+
const { pipeToTransform, readableStream, emitError } = transformRenderStreamChunksToResultObject(renderState);
|
|
21
|
+
const reportError = (error) => {
|
|
22
|
+
console.error('Error in RSC stream', error);
|
|
23
|
+
if (throwJsErrors) {
|
|
24
|
+
emitError(error);
|
|
25
|
+
}
|
|
26
|
+
renderState.hasErrors = true;
|
|
27
|
+
renderState.error = error;
|
|
28
|
+
};
|
|
29
|
+
const initializeAndRender = async () => {
|
|
30
|
+
if (!serverRendererPromise) {
|
|
31
|
+
serverRendererPromise = loadJsonFile(reactClientManifestFileName)
|
|
32
|
+
.then((reactClientManifest) => buildServerRenderer(reactClientManifest))
|
|
33
|
+
.catch((err) => {
|
|
34
|
+
serverRendererPromise = undefined;
|
|
35
|
+
throw err;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const { renderToPipeableStream } = await serverRendererPromise;
|
|
39
|
+
const rscStream = renderToPipeableStream(await reactRenderingResult, {
|
|
40
|
+
onError: (err) => {
|
|
41
|
+
const error = convertToError(err);
|
|
42
|
+
reportError(error);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
pipeToTransform(rscStream);
|
|
46
|
+
};
|
|
47
|
+
initializeAndRender().catch((e) => {
|
|
48
|
+
const error = convertToError(e);
|
|
49
|
+
reportError(error);
|
|
50
|
+
const errorHtml = handleError({ e: error, name: options.name, serverSide: true });
|
|
51
|
+
pipeToTransform(errorHtml);
|
|
52
|
+
});
|
|
53
|
+
readableStream.on('end', () => {
|
|
54
|
+
streamingTrackers.postSSRHookTracker.notifySSREnd();
|
|
55
|
+
});
|
|
56
|
+
return readableStream;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Pro RSC capability.
|
|
60
|
+
* Provides React Server Components rendering support.
|
|
61
|
+
*/
|
|
62
|
+
export function createProRSCCapability() {
|
|
63
|
+
return {
|
|
64
|
+
isRSCBundle: true,
|
|
65
|
+
serverRenderRSCReactComponent(options) {
|
|
66
|
+
try {
|
|
67
|
+
return streamServerRenderedComponent(options, streamRenderRSCComponent, handleError);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
console.history = [];
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
addAsyncPropsCapabilityToComponentProps(props, sharedExecutionContext) {
|
|
74
|
+
const asyncPropManager = getOrCreateAsyncPropsManager(sharedExecutionContext);
|
|
75
|
+
const propsAfterAddingAsyncProps = {
|
|
76
|
+
...props,
|
|
77
|
+
getReactOnRailsAsyncProp: (propName) => {
|
|
78
|
+
return asyncPropManager.getProp(propName);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
return { props: propsAfterAddingAsyncProps };
|
|
82
|
+
},
|
|
83
|
+
getOrCreateAsyncPropsManager(sharedExecutionContext) {
|
|
84
|
+
return getOrCreateAsyncPropsManager(sharedExecutionContext);
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=proRSC.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Readable } from 'stream';
|
|
2
|
+
import type { RenderParams } from 'react-on-rails/types';
|
|
3
|
+
/**
|
|
4
|
+
* Pro streaming capability.
|
|
5
|
+
* Provides server-side streaming rendering via streamServerRenderedReactComponent.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createProStreamingCapability(): {
|
|
8
|
+
streamServerRenderedReactComponent(options: RenderParams): Readable;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=proStreaming.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export -- named export for consistency with capability API */
|
|
2
|
+
import streamServerRenderedReactComponent from "../streamServerRenderedReactComponent.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pro streaming capability.
|
|
5
|
+
* Provides server-side streaming rendering via streamServerRenderedReactComponent.
|
|
6
|
+
*/
|
|
7
|
+
export function createProStreamingCapability() {
|
|
8
|
+
return {
|
|
9
|
+
streamServerRenderedReactComponent(options) {
|
|
10
|
+
return streamServerRenderedReactComponent(options);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=proStreaming.js.map
|