diff --git a/LICENSE.md b/LICENSE.md index 1d78ca5849..a6ba12b5c4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -11,7 +11,7 @@ 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 @@ -19,7 +19,6 @@ The following directories and all their contents are licensed under the **MIT Li 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) diff --git a/docs/MONOREPO_MERGER_PLAN.md b/docs/MONOREPO_MERGER_PLAN.md index ac4bf0054c..0a2ea1e0eb 100644 --- a/docs/MONOREPO_MERGER_PLAN.md +++ b/docs/MONOREPO_MERGER_PLAN.md @@ -372,12 +372,11 @@ After the initial merge, the following CI adjustments may be needed: ```md ## MIT License applies to: - - `lib/react_on_rails/` (excluding `lib/react_on_rails/pro/`) + - `lib/react_on_rails/` (entire directory) - `packages/react-on-rails/` (entire package) ## React on Rails Pro License applies to: - - `lib/react_on_rails/pro/` - `packages/react-on-rails-pro/` (entire package) - `react_on_rails_pro/` (entire directory) ``` diff --git a/lib/react_on_rails/helper.rb b/lib/react_on_rails/helper.rb index da6dd8995b..c37e81bd2c 100644 --- a/lib/react_on_rails/helper.rb +++ b/lib/react_on_rails/helper.rb @@ -11,12 +11,12 @@ require "react_on_rails/utils" require "react_on_rails/json_output" require "active_support/concern" -require "react_on_rails/pro/helper" +require "react_on_rails/pro_helper" module ReactOnRails module Helper include ReactOnRails::Utils::Required - include ReactOnRails::Pro::Helper + include ReactOnRails::ProHelper COMPONENT_HTML_KEY = "componentHtml" diff --git a/lib/react_on_rails/pro/NOTICE b/lib/react_on_rails/pro/NOTICE deleted file mode 100644 index 2c0b1bb4cd..0000000000 --- a/lib/react_on_rails/pro/NOTICE +++ /dev/null @@ -1,21 +0,0 @@ -# React on Rails Pro License - -The files in this directory and its subdirectories are licensed under the **React on Rails Pro** license, which is separate from the MIT license that covers the core React on Rails functionality. - -## License Terms - -These files are proprietary software and are **NOT** covered by the MIT license found in the root LICENSE.md file. Usage requires a valid React on Rails Pro license. - -## Distribution - -Files in this directory will be **omitted** from future distributions of the open source React on Rails Ruby gem. They are exclusively available to React on Rails Pro licensees. - -## License Reference - -For the complete React on Rails Pro license terms, see: `REACT-ON-RAILS-PRO-LICENSE.md` in the root directory of this repository. - -## More Information - -For React on Rails Pro licensing information and to obtain a license, please visit: -- [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/) -- Contact: [react_on_rails@shakacode.com](mailto:react_on_rails@shakacode.com) \ No newline at end of file diff --git a/lib/react_on_rails/pro/helper.rb b/lib/react_on_rails/pro/helper.rb deleted file mode 100644 index 54db997f03..0000000000 --- a/lib/react_on_rails/pro/helper.rb +++ /dev/null @@ -1,122 +0,0 @@ -# frozen_string_literal: true - -# /* -# * Copyright (c) 2025 Shakacode LLC -# * -# * This file is NOT licensed under the MIT (open source) license. -# * It is part of the React on Rails Pro offering and is licensed separately. -# * -# * Unauthorized copying, modification, distribution, or use of this file, -# * via any medium, is strictly prohibited without a valid license agreement -# * from Shakacode LLC. -# * -# * For licensing terms, please see: -# * https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md -# */ - -module ReactOnRails - module Pro - module Helper - IMMEDIATE_HYDRATION_PRO_WARNING = "[REACT ON RAILS] The 'immediate_hydration' feature requires a " \ - "React on Rails Pro license. " \ - "Please visit https://shakacode.com/react-on-rails-pro to learn more." - - # Generates the complete component specification script tag. - # Handles both immediate hydration (Pro feature) and standard cases. - def generate_component_script(render_options) - # Setup the page_loaded_js, which is the same regardless of prerendering or not! - # The reason is that React is smart about not doing extra work if the server rendering did its job. - component_specification_tag = content_tag(:script, - json_safe_and_pretty(render_options.client_props).html_safe, - 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, - "data-immediate-hydration" => - (render_options.immediate_hydration ? true : nil)) - - # Add immediate invocation script if immediate hydration is enabled - spec_tag = if render_options.immediate_hydration - # Escape dom_id for JavaScript context - escaped_dom_id = escape_javascript(render_options.dom_id) - immediate_script = content_tag(:script, %( - typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsComponentLoaded('#{escaped_dom_id}'); - ).html_safe) - "#{component_specification_tag}\n#{immediate_script}" - else - component_specification_tag - end - - pro_warning_badge = pro_warning_badge_if_needed(render_options.explicitly_disabled_pro_options) - "#{pro_warning_badge}\n#{spec_tag}".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) - pro_options_check_result = ReactOnRails::Pro::Utils.disable_pro_render_options_if_not_licensed(redux_store_data) - redux_store_data = pro_options_check_result[:raw_options] - explicitly_disabled_pro_options = pro_options_check_result[:explicitly_disabled_pro_options] - - store_hydration_data = content_tag(:script, - json_safe_and_pretty(redux_store_data[:props]).html_safe, - type: "application/json", - "data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe, - "data-immediate-hydration" => - (redux_store_data[:immediate_hydration] ? true : nil)) - - # Add immediate invocation script if immediate hydration is enabled and Pro license is valid - store_hydration_scripts = if redux_store_data[:immediate_hydration] - # Escape store_name for JavaScript context - escaped_store_name = escape_javascript(redux_store_data[:store_name]) - immediate_script = content_tag(:script, <<~JS.strip_heredoc.html_safe - typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsStoreLoaded('#{escaped_store_name}'); - JS - ) - "#{store_hydration_data}\n#{immediate_script}" - else - store_hydration_data - end - - pro_warning_badge = pro_warning_badge_if_needed(explicitly_disabled_pro_options) - "#{pro_warning_badge}\n#{store_hydration_scripts}".html_safe - end - - def pro_warning_badge_if_needed(explicitly_disabled_pro_options) - return "" unless explicitly_disabled_pro_options.any? - - disabled_features_message = disabled_pro_features_message(explicitly_disabled_pro_options) - warning_message = "[REACT ON RAILS] #{disabled_features_message}\n" \ - "Please visit https://shakacode.com/react-on-rails-pro to learn more." - puts warning_message - Rails.logger.warn warning_message - - tooltip_text = "#{disabled_features_message} Click to learn more." - - <<~HTML.strip - -
-
- React On Rails Pro Required -
-
-
- HTML - end - - def disabled_pro_features_message(explicitly_disabled_pro_options) - return "".html_safe unless explicitly_disabled_pro_options.any? - - feature_list = explicitly_disabled_pro_options.join(", ") - feature_word = explicitly_disabled_pro_options.size == 1 ? "feature" : "features" - "The '#{feature_list}' #{feature_word} " \ - "#{explicitly_disabled_pro_options.size == 1 ? 'requires' : 'require'} a " \ - "React on Rails Pro license. " - end - end - end -end diff --git a/lib/react_on_rails/pro/utils.rb b/lib/react_on_rails/pro/utils.rb deleted file mode 100644 index e98a4c27e8..0000000000 --- a/lib/react_on_rails/pro/utils.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -# /* -# * Copyright (c) 2025 Shakacode LLC -# * -# * This file is NOT licensed under the MIT (open source) license. -# * It is part of the React on Rails Pro offering and is licensed separately. -# * -# * Unauthorized copying, modification, distribution, or use of this file, -# * via any medium, is strictly prohibited without a valid license agreement -# * from Shakacode LLC. -# * -# * For licensing terms, please see: -# * https://github.com/shakacode/react_on_rails/blob/master/REACT-ON-RAILS-PRO-LICENSE.md -# */ - -module ReactOnRails - module Pro - module Utils - PRO_ONLY_OPTIONS = %i[immediate_hydration].freeze - - # Checks if React on Rails Pro features are available - # @return [Boolean] true if Pro license is valid, false otherwise - def self.support_pro_features? - ReactOnRails::Utils.react_on_rails_pro_licence_valid? - end - - def self.disable_pro_render_options_if_not_licensed(raw_options) - if support_pro_features? - return { - raw_options: raw_options, - explicitly_disabled_pro_options: [] - } - end - - raw_options_after_disable = raw_options.dup - - explicitly_disabled_pro_options = PRO_ONLY_OPTIONS.select do |option| - # Use global configuration if it's not overridden in the options - next ReactOnRails.configuration.send(option) if raw_options[option].nil? - - raw_options[option] - end - explicitly_disabled_pro_options.each { |option| raw_options_after_disable[option] = false } - - { - raw_options: raw_options_after_disable, - explicitly_disabled_pro_options: explicitly_disabled_pro_options - } - end - end - end -end diff --git a/lib/react_on_rails/pro_helper.rb b/lib/react_on_rails/pro_helper.rb new file mode 100644 index 0000000000..41d0a51763 --- /dev/null +++ b/lib/react_on_rails/pro_helper.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module ReactOnRails + module ProHelper + IMMEDIATE_HYDRATION_PRO_WARNING = "[REACT ON RAILS] The 'immediate_hydration' feature requires a " \ + "React on Rails Pro license. " \ + "Please visit https://shakacode.com/react-on-rails-pro to learn more." + + # Generates the complete component specification script tag. + # Handles both immediate hydration (Pro feature) and standard cases. + def generate_component_script(render_options) + # Setup the page_loaded_js, which is the same regardless of prerendering or not! + # The reason is that React is smart about not doing extra work if the server rendering did its job. + component_specification_tag = content_tag(:script, + json_safe_and_pretty(render_options.client_props).html_safe, + 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, + "data-immediate-hydration" => + (render_options.immediate_hydration ? true : nil)) + + # Add immediate invocation script if immediate hydration is enabled + spec_tag = if render_options.immediate_hydration + # Escape dom_id for JavaScript context + escaped_dom_id = escape_javascript(render_options.dom_id) + immediate_script = content_tag(:script, %( + typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsComponentLoaded('#{escaped_dom_id}'); + ).html_safe) + "#{component_specification_tag}\n#{immediate_script}" + else + component_specification_tag + end + + pro_warning_badge = pro_warning_badge_if_needed(render_options.explicitly_disabled_pro_options) + "#{pro_warning_badge}\n#{spec_tag}".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) + pro_options_check_result = ReactOnRails::ProUtils.disable_pro_render_options_if_not_licensed(redux_store_data) + redux_store_data = pro_options_check_result[:raw_options] + explicitly_disabled_pro_options = pro_options_check_result[:explicitly_disabled_pro_options] + + store_hydration_data = content_tag(:script, + json_safe_and_pretty(redux_store_data[:props]).html_safe, + type: "application/json", + "data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe, + "data-immediate-hydration" => + (redux_store_data[:immediate_hydration] ? true : nil)) + + # Add immediate invocation script if immediate hydration is enabled and Pro license is valid + store_hydration_scripts = if redux_store_data[:immediate_hydration] + # Escape store_name for JavaScript context + escaped_store_name = escape_javascript(redux_store_data[:store_name]) + immediate_script = content_tag(:script, <<~JS.strip_heredoc.html_safe + typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsStoreLoaded('#{escaped_store_name}'); + JS + ) + "#{store_hydration_data}\n#{immediate_script}" + else + store_hydration_data + end + + pro_warning_badge = pro_warning_badge_if_needed(explicitly_disabled_pro_options) + "#{pro_warning_badge}\n#{store_hydration_scripts}".html_safe + end + + def pro_warning_badge_if_needed(explicitly_disabled_pro_options) + return "" unless explicitly_disabled_pro_options.any? + + disabled_features_message = disabled_pro_features_message(explicitly_disabled_pro_options) + warning_message = "[REACT ON RAILS] #{disabled_features_message}\n" \ + "Please visit https://shakacode.com/react-on-rails-pro to learn more." + puts warning_message + Rails.logger.warn warning_message + + tooltip_text = "#{disabled_features_message} Click to learn more." + + <<~HTML.strip + +
+
+ React On Rails Pro Required +
+
+
+ HTML + end + + def disabled_pro_features_message(explicitly_disabled_pro_options) + return "".html_safe unless explicitly_disabled_pro_options.any? + + feature_list = explicitly_disabled_pro_options.join(", ") + feature_word = explicitly_disabled_pro_options.size == 1 ? "feature" : "features" + "The '#{feature_list}' #{feature_word} " \ + "#{explicitly_disabled_pro_options.size == 1 ? 'requires' : 'require'} a " \ + "React on Rails Pro license. " + end + end +end diff --git a/lib/react_on_rails/pro_utils.rb b/lib/react_on_rails/pro_utils.rb new file mode 100644 index 0000000000..030e5ece8b --- /dev/null +++ b/lib/react_on_rails/pro_utils.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module ReactOnRails + module ProUtils + PRO_ONLY_OPTIONS = %i[immediate_hydration].freeze + + # Checks if React on Rails Pro features are available + # @return [Boolean] true if Pro license is valid, false otherwise + def self.support_pro_features? + ReactOnRails::Utils.react_on_rails_pro_licence_valid? + end + + def self.disable_pro_render_options_if_not_licensed(raw_options) + if support_pro_features? + return { + raw_options: raw_options, + explicitly_disabled_pro_options: [] + } + end + + raw_options_after_disable = raw_options.dup + + explicitly_disabled_pro_options = PRO_ONLY_OPTIONS.select do |option| + # Use global configuration if it's not overridden in the options + next ReactOnRails.configuration.send(option) if raw_options[option].nil? + + raw_options[option] + end + explicitly_disabled_pro_options.each { |option| raw_options_after_disable[option] = false } + + { + raw_options: raw_options_after_disable, + explicitly_disabled_pro_options: explicitly_disabled_pro_options + } + end + end +end diff --git a/lib/react_on_rails/react_component/render_options.rb b/lib/react_on_rails/react_component/render_options.rb index 9c1a8a3986..3d9a4cdf05 100644 --- a/lib/react_on_rails/react_component/render_options.rb +++ b/lib/react_on_rails/react_component/render_options.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "react_on_rails/utils" -require "react_on_rails/pro/utils" +require "react_on_rails/pro_utils" module ReactOnRails module ReactComponent @@ -16,7 +16,7 @@ class RenderOptions def initialize(react_component_name: required("react_component_name"), options: required("options")) @react_component_name = react_component_name.camelize - result = ReactOnRails::Pro::Utils.disable_pro_render_options_if_not_licensed(options) + result = ReactOnRails::ProUtils.disable_pro_render_options_if_not_licensed(options) @options = result[:raw_options] @explicitly_disabled_pro_options = result[:explicitly_disabled_pro_options] end