Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ This repository contains code under two different licenses:

The following directories and all their contents are licensed under the **MIT License** (see full text below):

- `lib/react_on_rails/` (excluding `lib/react_on_rails/pro/`)
- `lib/react_on_rails/` (entire directory)
- `packages/react-on-rails/` (entire package)
- All other directories in this repository not explicitly listed as Pro-licensed

### Pro Licensed Code

The following directories and all their contents are licensed under the **React on Rails Pro License**:

- `lib/react_on_rails/pro/`
- `packages/react-on-rails-pro/` (entire package)
- `react_on_rails_pro/` (entire directory)

Expand Down
75 changes: 71 additions & 4 deletions docs/MONOREPO_MERGER_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,74 @@ After the initial merge, the following CI adjustments may be needed:

---

#### PR #5: Add Pro Node Renderer Package
#### PR #5: Move Pro Features from Core Gem to Pro Gem

**Branch:** `move-pro-features-to-pro-gem`

**Objectives:**

- Move all Pro features from `lib/react_on_rails/pro/` to Pro gem
- Delete `lib/react_on_rails/pro/` directory entirely
- Ensure core gem is 100% MIT licensed with zero Pro code

**Tasks:**

- [x] Move `immediate_hydration` config from core gem to Pro gem (default: true in Pro)
- [x] Refactor `RenderOptions` to remove Pro utilities
- [x] Refactor helper methods (`generate_component_script`, `generate_store_script`) to use data enhancement pattern
- [x] Create Pro helper module in Pro gem with enhancement methods
- [x] Delete `lib/react_on_rails/pro/` directory entirely
- [x] Update LICENSE.md to remove `lib/react_on_rails/pro/` reference
- [x] Update tests in both gems

**Implementation Details:**

The `immediate_hydration` feature was the only Pro feature in the core gem. The refactoring uses a data enhancement pattern:

1. Core gem collects script attributes/content as data structures (not HTML)
2. If Pro gem loaded, it modifies the data (adds attributes, adds extra scripts)
3. Core gem generates final HTML from the (possibly enhanced) data

**Benefits:**

- ✅ Clean separation: Core gem = 100% MIT, Pro gem = 100% Pro license
- ✅ No HTML parsing needed
- ✅ No Pro warning badge needed (can't enable Pro features without Pro gem)
- ✅ Better architecture: Core gem doesn't know about Pro internals

**License Compliance:**

- [x] **CRITICAL: Update LICENSE.md:**

```md
## MIT License applies to:

- `lib/react_on_rails/` (entire directory)
- `packages/react-on-rails/` (entire package)

## React on Rails Pro License applies to:

- `packages/react-on-rails-pro/` (entire package)
- `react_on_rails_pro/` (entire directory)
```

- [x] Verify no Pro code remains in core gem directories

**Success Criteria:** ✅ All CI checks pass + `lib/react_on_rails/pro/` deleted + Core gem is 100% MIT licensed

**Estimated Duration:** 2-3 days

**Risk Level:** Medium (requires careful refactoring of helper methods)

**Developer Notes:**

- The core gem now calls `ReactOnRailsPro::Helper.enhance_component_script_data` and `ReactOnRailsPro::Helper.enhance_store_script_data` if Pro gem is loaded
- The `immediate_hydration` method in `RenderOptions` now uses `retrieve_react_on_rails_pro_config_value_for(:immediate_hydration)`
- Tests have been updated to mock Pro gem functionality

---
Comment on lines +407 to +472
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore the missing phase heading before PR #5.

After introducing this new PR, the document skips a “Phase 5” heading and jumps straight to “Phase 6,” which now contradicts the stated 8-phase plan. Please add the appropriate phase heading (e.g., ### Phase 5: ...) above this block so the numbering stays consistent.

🤖 Prompt for AI Agents
In docs/MONOREPO_MERGER_PLAN.md around lines 407 to 472, the document jumps from
Phase 4 to Phase 6 because the Phase 5 heading is missing; insert an appropriate
Phase 5 heading (for example: "### Phase 5: Move Pro features into Pro gem /
Core cleanup") immediately above the "PR #5: Move Pro Features from Core Gem to
Pro Gem" block so the numbering is consistent with the 8-phase plan and the
section is clearly labeled.


#### PR #6: Add Pro Node Renderer Package

**Branch:** `add-pro-node-renderer`

Expand Down Expand Up @@ -455,7 +522,7 @@ After the initial merge, the following CI adjustments may be needed:

### Phase 6: Final Monorepo Restructuring

#### PR #6: Restructure Ruby Gems to Final Layout
#### PR #7: Restructure Ruby Gems to Final Layout

**Branch:** `restructure-ruby-gems`

Expand Down Expand Up @@ -522,7 +589,7 @@ After the initial merge, the following CI adjustments may be needed:

### Phase 7: CI/CD & Tooling Unification

#### PR #7: Unify CI/CD Configuration
#### PR #8: Unify CI/CD Configuration

**Branch:** `unify-cicd`

Expand Down Expand Up @@ -588,7 +655,7 @@ After the initial merge, the following CI adjustments may be needed:

### Phase 8: Documentation & Polish

#### PR #8: Update Documentation & Examples
#### PR #9: Update Documentation & Examples

**Branch:** `update-docs-examples`

Expand Down
7 changes: 2 additions & 5 deletions lib/react_on_rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ def self.configuration
components_subdirectory: nil,
make_generated_server_bundle_the_entrypoint: false,
defer_generated_component_packs: false,
# React on Rails Pro (licensed) feature - enables immediate hydration of React components
immediate_hydration: false,
# Maximum time in milliseconds to wait for client-side component registration after page load.
# If exceeded, an error will be thrown for server-side rendered components not registered on the client.
# Set to 0 to disable the timeout and wait indefinitely for component registration.
Expand All @@ -72,7 +70,7 @@ class Configuration
:server_render_method, :random_dom_id, :auto_load_bundle,
:same_bundle_for_client_and_server, :rendering_props_extension,
:make_generated_server_bundle_the_entrypoint,
:generated_component_packs_loading_strategy, :immediate_hydration, :rsc_bundle_js_file,
:generated_component_packs_loading_strategy, :rsc_bundle_js_file,
:react_client_manifest_file, :react_server_client_manifest_file, :component_registry_timeout,
:server_bundle_output_path, :enforce_private_server_bundles

Expand All @@ -89,7 +87,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
same_bundle_for_client_and_server: nil,
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil,
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
components_subdirectory: nil, auto_load_bundle: nil, immediate_hydration: nil,
components_subdirectory: nil, auto_load_bundle: nil,
rsc_bundle_js_file: nil, react_client_manifest_file: nil, react_server_client_manifest_file: nil,
component_registry_timeout: nil, server_bundle_output_path: nil, enforce_private_server_bundles: nil)
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
Expand Down Expand Up @@ -134,7 +132,6 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
self.auto_load_bundle = auto_load_bundle
self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint
self.defer_generated_component_packs = defer_generated_component_packs
self.immediate_hydration = immediate_hydration
self.generated_component_packs_loading_strategy = generated_component_packs_loading_strategy
self.server_bundle_output_path = server_bundle_output_path
self.enforce_private_server_bundles = enforce_private_server_bundles
Expand Down
5 changes: 4 additions & 1 deletion lib/react_on_rails/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ module Controller
# Be sure to include view helper `redux_store_hydration_data` at the end of your layout or view
# or else there will be no client side hydration of your stores.
def redux_store(store_name, props: {}, immediate_hydration: nil)
immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil?
if immediate_hydration.nil? && ReactOnRails::Utils.react_on_rails_pro?
immediate_hydration = ReactOnRailsPro.configuration.immediate_hydration
end
immediate_hydration = false if immediate_hydration.nil?
redux_store_data = { store_name: store_name,
props: props,
immediate_hydration: immediate_hydration }
Expand Down
69 changes: 66 additions & 3 deletions lib/react_on_rails/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@
require "react_on_rails/utils"
require "react_on_rails/json_output"
require "active_support/concern"
require "react_on_rails/pro/helper"

module ReactOnRails
module Helper
include ReactOnRails::Utils::Required
include ReactOnRails::Pro::Helper

COMPONENT_HTML_KEY = "componentHtml"

Expand Down Expand Up @@ -255,7 +253,10 @@ def react_component_hash(component_name, options = {})
# immediate_hydration: false -- React on Rails Pro (licensed) feature. Pass as true if you wish to
# hydrate this store immediately instead of waiting for the page to load.
def redux_store(store_name, props: {}, defer: false, immediate_hydration: nil)
immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil?
if immediate_hydration.nil? && ReactOnRails::Utils.react_on_rails_pro?
immediate_hydration = ReactOnRailsPro.configuration.immediate_hydration
end
immediate_hydration = false if immediate_hydration.nil?

redux_store_data = { store_name: store_name,
props: props,
Expand Down Expand Up @@ -793,6 +794,68 @@ def in_mailer?
instrument_method :react_component_hash, type: "ReactOnRails", name: "react_component_hash"
end

# Generates the complete component specification script tag.
# Handles both immediate hydration (Pro feature) and standard cases.
def generate_component_script(render_options)
# Collect script data
script_attrs = {
type: "application/json",
class: "js-react-on-rails-component",
id: "js-react-on-rails-component-#{render_options.dom_id}",
"data-component-name" => render_options.react_component_name,
"data-trace" => (render_options.trace ? true : nil),
"data-dom-id" => render_options.dom_id,
"data-store-dependencies" => render_options.store_dependencies&.to_json
}

script_content = json_safe_and_pretty(render_options.client_props).html_safe
additional_scripts = []

# Let Pro gem enhance if available
if ReactOnRails::Utils.react_on_rails_pro?
result = ReactOnRailsPro::Helper.enhance_component_script_data(
script_attrs: script_attrs,
script_content: script_content,
render_options: render_options
)
script_attrs = result[:script_attrs]
script_content = result[:script_content]
additional_scripts = result[:additional_scripts]
end

# Generate final HTML
main_script = content_tag(:script, script_content, script_attrs)
([main_script] + additional_scripts).join("\n").html_safe
end

# Generates the complete store hydration script tag.
# Handles both immediate hydration (Pro feature) and standard cases.
def generate_store_script(redux_store_data)
script_attrs = {
type: "application/json",
"data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe
}

script_content = json_safe_and_pretty(redux_store_data[:props]).html_safe
additional_scripts = []

# Let Pro gem enhance if available
if ReactOnRails::Utils.react_on_rails_pro?
result = ReactOnRailsPro::Helper.enhance_store_script_data(
script_attrs: script_attrs,
script_content: script_content,
redux_store_data: redux_store_data
)
script_attrs = result[:script_attrs]
script_content = result[:script_content]
additional_scripts = result[:additional_scripts]
end

# Generate final HTML
main_script = content_tag(:script, script_content, script_attrs)
([main_script] + additional_scripts).join("\n").html_safe
end

def raise_missing_autoloaded_bundle(react_component_name)
msg = <<~MSG
**ERROR** ReactOnRails: Component "#{react_component_name}" is configured as "auto_load_bundle: true"
Expand Down
21 changes: 0 additions & 21 deletions lib/react_on_rails/pro/NOTICE

This file was deleted.

Loading
Loading