Skip to content

Commit 9c349ea

Browse files
Update licensing information and enhance Pro features integration
- Updated LICENSE.md to clarify the MIT License applicability and introduce the React on Rails Pro License for specific directories. - Refactored helper methods in `lib/react_on_rails/helper.rb` to utilize new Pro features, including immediate hydration support. - Introduced `lib/react_on_rails/pro_features/helper.rb` to encapsulate Pro feature logic, including immediate hydration and warning badge generation. - Adjusted imports in `node_package/src/ReactOnRails.node.ts` to reflect the new Pro directory structure. - Added new files for React Server Component handling and RSC payload management in the Pro directory, enhancing server-side rendering capabilities. - Improved error handling and stream management for RSC payloads to optimize performance and debugging.
1 parent e3215ed commit 9c349ea

23 files changed

+146
-93
lines changed

LICENSE.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55

66
---
77

8-
# MIT License
8+
## MIT License for Core React on Rails
9+
10+
This license applies to all files within this repository, with the exception of the code located in the following directories, which are licensed separately under the React on Rails Pro License:
11+
12+
- `lib/react_on_rails/pro_features/`
13+
- `node_package/src/pro/`
914

1015
Copyright (c) 2017, 2018 Justin Gordon and ShakaCode
1116
Copyright (c) 2015–2025 ShakaCode, LLC
@@ -31,3 +36,12 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3136
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3237
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3338
SOFTWARE.
39+
40+
---
41+
42+
## React on Rails Pro License
43+
44+
The code in the directories listed above is part of the React on Rails Pro framework and is licensed under the React on Rails Pro License.
45+
46+
You can find the full text of the license agreement here:
47+
[REACT-ON-RAILS-PRO-LICENSE.md](./REACT-ON-RAILS-PRO-LICENSE.md)

lib/react_on_rails/helper.rb

Lines changed: 8 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@
1111
require "react_on_rails/utils"
1212
require "react_on_rails/json_output"
1313
require "active_support/concern"
14+
require "react_on_rails/pro_features/helper"
1415

1516
module ReactOnRails
1617
module Helper
1718
include ReactOnRails::Utils::Required
19+
include ReactOnRails::ProFeatures::Helper
1820

1921
COMPONENT_HTML_KEY = "componentHtml"
20-
IMMEDIATE_HYDRATION_PRO_WARNING = "[REACT ON RAILS] The 'immediate_hydration' feature requires a " \
21-
"React on Rails Pro license. " \
22-
"Please visit https://shakacode.com/react-on-rails-pro to learn more."
2322

2423
# react_component_name: can be a React function or class component or a "Render-Function".
2524
# "Render-Functions" differ from a React function in that they take two parameters, the
@@ -452,32 +451,6 @@ def load_pack_for_generated_component(react_component_name, render_options)
452451

453452
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
454453

455-
# Checks if React on Rails Pro features are available
456-
# @return [Boolean] true if Pro license is valid, false otherwise
457-
def support_pro_features?
458-
ReactOnRails::Utils.react_on_rails_pro_licence_valid?
459-
end
460-
461-
def pro_warning_badge_if_needed(immediate_hydration)
462-
return "".html_safe unless immediate_hydration
463-
return "".html_safe if support_pro_features?
464-
465-
puts IMMEDIATE_HYDRATION_PRO_WARNING
466-
Rails.logger.warn IMMEDIATE_HYDRATION_PRO_WARNING
467-
468-
tooltip_text = "The 'immediate_hydration' feature requires a React on Rails Pro license. Click to learn more."
469-
470-
badge_html = <<~HTML
471-
<a href="https://shakacode.com/react-on-rails-pro" target="_blank" rel="noopener noreferrer" title="#{tooltip_text}">
472-
<div style="position: fixed; top: 0; right: 0; width: 180px; height: 180px; overflow: hidden; z-index: 9999; pointer-events: none;">
473-
<div style="position: absolute; top: 50px; right: -40px; transform: rotate(45deg); background-color: rgba(220, 53, 69, 0.85); color: white; padding: 7px 40px; text-align: center; font-weight: bold; font-family: sans-serif; font-size: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); pointer-events: auto;">
474-
React On Rails Pro Required
475-
</div>
476-
</div>
477-
</a>
478-
HTML
479-
badge_html.strip.html_safe
480-
end
481454

482455
def run_stream_inside_fiber
483456
unless ReactOnRails::Utils.react_on_rails_pro?
@@ -689,17 +662,9 @@ def internal_react_component(react_component_name, options = {})
689662
"data-component-name" => render_options.react_component_name,
690663
"data-trace" => (render_options.trace ? true : nil),
691664
"data-dom-id" => render_options.dom_id,
692-
"data-store-dependencies" => render_options.store_dependencies&.to_json,
693-
"data-immediate-hydration" =>
694-
(render_options.immediate_hydration ? true : nil))
695-
696-
if render_options.immediate_hydration
697-
component_specification_tag.concat(
698-
content_tag(:script, %(
699-
typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsComponentLoaded('#{render_options.dom_id}');
700-
).html_safe)
701-
)
702-
end
665+
"data-store-dependencies" => render_options.store_dependencies&.to_json)
666+
667+
component_specification_tag = apply_immediate_hydration_if_supported(component_specification_tag, render_options)
703668

704669
load_pack_for_generated_component(react_component_name, render_options)
705670
# Create the HTML rendering part
@@ -717,18 +682,9 @@ def render_redux_store_data(redux_store_data)
717682
store_hydration_data = content_tag(:script,
718683
json_safe_and_pretty(redux_store_data[:props]).html_safe,
719684
type: "application/json",
720-
"data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe,
721-
"data-immediate-hydration" =>
722-
(redux_store_data[:immediate_hydration] ? true : nil))
723-
724-
if redux_store_data[:immediate_hydration]
725-
store_hydration_data.concat(
726-
content_tag(:script, <<~JS.strip_heredoc.html_safe
727-
typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsStoreLoaded('#{redux_store_data[:store_name]}');
728-
JS
729-
)
730-
)
731-
end
685+
"data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe)
686+
687+
store_hydration_data = apply_store_immediate_hydration_if_supported(store_hydration_data, redux_store_data)
732688

733689
prepend_render_rails_context(store_hydration_data)
734690
end
@@ -874,5 +830,3 @@ def raise_missing_autoloaded_bundle(react_component_name)
874830
end
875831
end
876832
end
877-
# rubocop:enable Metrics/ModuleLength
878-
# rubocop:enable Metrics/MethodLength
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# frozen_string_literal: true
2+
3+
# /*
4+
# * Copyright (c) 2025 Shakacode
5+
# *
6+
# * This file, and all other files in this directory, are NOT licensed under the MIT license.
7+
# *
8+
# * This file is part of React on Rails Pro.
9+
# *
10+
# * Unauthorized copying, modification, distribution, or use of this file, via any medium,
11+
# * is strictly prohibited. It is proprietary and confidential.
12+
# *
13+
# * For the full license agreement, see:
14+
# * https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md
15+
# */
16+
17+
module ReactOnRails
18+
module ProFeatures
19+
module Helper
20+
IMMEDIATE_HYDRATION_PRO_WARNING = "[REACT ON RAILS] The 'immediate_hydration' feature requires a " \
21+
"React on Rails Pro license. " \
22+
"Please visit https://shakacode.com/react-on-rails-pro to learn more."
23+
24+
# This method is responsible for generating the necessary attributes and script tags
25+
# for the immediate_hydration feature. It is enabled only when a valid
26+
# React on Rails Pro license is detected.
27+
def apply_immediate_hydration_if_supported(component_specification_tag, render_options)
28+
return component_specification_tag unless render_options.immediate_hydration && support_pro_features?
29+
30+
# Add data attribute
31+
component_specification_tag.gsub!(/>\z/, ' data-immediate-hydration="true">')
32+
33+
# Add immediate invocation script
34+
component_specification_tag.concat(
35+
content_tag(:script, %(
36+
typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsComponentLoaded('#{render_options.dom_id}');
37+
).html_safe)
38+
)
39+
end
40+
41+
# Similar logic for redux_store
42+
def apply_store_immediate_hydration_if_supported(store_hydration_data, redux_store_data)
43+
return store_hydration_data unless redux_store_data[:immediate_hydration] && support_pro_features?
44+
45+
# Add data attribute
46+
store_hydration_data.gsub!(/>\z/, ' data-immediate-hydration="true">')
47+
48+
# Add immediate invocation script
49+
store_hydration_data.concat(
50+
content_tag(:script, <<~JS.strip_heredoc.html_safe
51+
typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsStoreLoaded('#{redux_store_data[:store_name]}');
52+
JS
53+
)
54+
)
55+
end
56+
57+
# Checks if React on Rails Pro features are available
58+
# @return [Boolean] true if Pro license is valid, false otherwise
59+
def support_pro_features?
60+
ReactOnRails::Utils.react_on_rails_pro_licence_valid?
61+
end
62+
63+
def pro_warning_badge_if_needed(immediate_hydration)
64+
return "".html_safe unless immediate_hydration
65+
return "".html_safe if support_pro_features?
66+
67+
puts IMMEDIATE_HYDRATION_PRO_WARNING
68+
Rails.logger.warn IMMEDIATE_HYDRATION_PRO_WARNING
69+
70+
tooltip_text = "The 'immediate_hydration' feature requires a React on Rails Pro license. Click to learn more."
71+
72+
badge_html = <<~HTML
73+
<a href="https://shakacode.com/react-on-rails-pro" target="_blank" rel="noopener noreferrer" title="#{tooltip_text}">
74+
<div style="position: fixed; top: 0; right: 0; width: 180px; height: 180px; overflow: hidden; z-index: 9999; pointer-events: none;">
75+
<div style="position: absolute; top: 50px; right: -40px; transform: rotate(45deg); background-color: rgba(220, 53, 69, 0.85); color: white; padding: 7px 40px; text-align: center; font-weight: bold; font-family: sans-serif; font-size: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); pointer-events: auto;">
76+
React On Rails Pro Required
77+
</div>
78+
</div>
79+
</a>
80+
HTML
81+
badge_html.strip.html_safe
82+
end
83+
end
84+
end
85+
end

node_package/src/ReactOnRails.node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import ReactOnRails from './ReactOnRails.full.ts';
2-
import streamServerRenderedReactComponent from './streamServerRenderedReactComponent.ts';
2+
import streamServerRenderedReactComponent from './pro/streamServerRenderedReactComponent.ts';
33

44
ReactOnRails.streamServerRenderedReactComponent = streamServerRenderedReactComponent;
55

node_package/src/pro/ClientSideRenderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable max-classes-per-file */
22

33
import type { ReactElement } from 'react';
4-
import type { RailsContext, RegisteredComponent, RenderFunction, Root } from '../types/index';
4+
import type { RailsContext, RegisteredComponent, RenderFunction, Root } from '../types/index.ts';
55

66
import { getRailsContext, resetRailsContext } from '../context.ts';
77
import createReactOutput from '../createReactOutput.ts';

node_package/src/RSCProvider.tsx renamed to node_package/src/pro/RSCProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import type { ClientGetReactServerComponentProps } from './getReactServerComponent.client.ts';
3-
import { createRSCPayloadKey } from './utils.ts';
3+
import { createRSCPayloadKey } from '../utils.ts';
44

55
type RSCContextType = {
66
getComponent: (componentName: string, componentProps: unknown) => Promise<React.ReactNode>;

node_package/src/RSCRequestTracker.ts renamed to node_package/src/pro/RSCRequestTracker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { PassThrough, Readable } from 'stream';
2-
import { extractErrorMessage } from './utils.ts';
2+
import { extractErrorMessage } from '../utils.ts';
33
import {
44
RSCPayloadStreamInfo,
55
RSCPayloadCallback,
66
RailsContextWithServerComponentMetadata,
7-
} from './types/index.ts';
7+
} from '../types/index.ts';
88

99
/**
1010
* Global function provided by React on Rails Pro for generating RSC payloads.
File renamed without changes.

node_package/src/ReactOnRailsRSC.ts renamed to node_package/src/pro/ReactOnRailsRSC.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ import {
77
assertRailsContextWithServerStreamingCapabilities,
88
StreamRenderState,
99
StreamableComponentResult,
10-
} from './types/index.ts';
11-
import ReactOnRails from './ReactOnRails.full.ts';
12-
import handleError from './handleError.ts';
13-
import { convertToError } from './serverRenderUtils.ts';
10+
} from '../types/index.ts';
11+
import ReactOnRails from '../ReactOnRails.full.ts';
12+
import handleError from '../handleError.ts';
13+
import { convertToError } from '../serverRenderUtils.ts';
1414

1515
import {
1616
streamServerRenderedComponent,
1717
StreamingTrackers,
1818
transformRenderStreamChunksToResultObject,
1919
} from './streamServerRenderedReactComponent.ts';
20-
import loadJsonFile from './loadJsonFile.ts';
20+
import loadJsonFile from '../loadJsonFile.ts';
2121

2222
let serverRendererPromise: Promise<ReturnType<typeof buildServerRenderer>> | undefined;
2323

@@ -93,5 +93,5 @@ ReactOnRails.serverRenderRSCReactComponent = (options: RSCRenderParams) => {
9393

9494
ReactOnRails.isRSCBundle = true;
9595

96-
export * from './types/index.ts';
96+
export * from '../types/index.ts';
9797
export default ReactOnRails;

0 commit comments

Comments
 (0)