Class: ReactOnRails::Engine
- Inherits:
-
Rails::Engine
- Object
- Rails::Engine
- ReactOnRails::Engine
- Defined in:
- lib/react_on_rails/engine.rb
Constant Summary collapse
- SHAKAPACKER_PACKAGE_MANAGER_CHECK =
:error_unless_package_manager_is_obvious!- SHAKAPACKER_MANAGER_GUARD_ISSUE_URL =
"https://github.com/shakacode/react_on_rails/issues/3145"
Class Method Summary collapse
- .fetch_shakapacker_package_manager_guard_method(manager) ⇒ Object
- .install_shakapacker_package_manager_check_wrapper ⇒ Object
- .log_shakapacker_guard_warning(message) ⇒ Object
-
.package_json_missing? ⇒ Boolean
Check if package.json doesn’t exist yet.
-
.running_generator? ⇒ Boolean
Check if we’re running a Rails generator Heuristic: Rails::Generators is typically only defined during generator commands.
-
.shakapacker_config_path ⇒ Object
Resolves the Shakapacker config path, mirroring how Shakapacker itself locates the file.
-
.shakapacker_configured_as_bundler? ⇒ Boolean
Returns true when the host app has a Shakapacker config file, meaning Shakapacker is the configured bundler.
- .shakapacker_utils_manager ⇒ Object
-
.skip_version_validation? ⇒ Boolean
Determine if version validation should be skipped.
-
.suppress_shakapacker_package_manager_check_if_not_bundler! ⇒ Object
Wrap Shakapacker’s package-manager guard when Shakapacker is not the bundler.
-
.warn_if_shakapacker_env_config_missing ⇒ Object
Warn at boot when SHAKAPACKER_CONFIG points to a missing file so typos or not-yet-created paths surface clearly.
Class Method Details
.fetch_shakapacker_package_manager_guard_method(manager) ⇒ Object
186 187 188 189 190 191 192 193 |
# File 'lib/react_on_rails/engine.rb', line 186 def self.fetch_shakapacker_package_manager_guard_method(manager) return manager.method(SHAKAPACKER_PACKAGE_MANAGER_CHECK) if manager.respond_to?(SHAKAPACKER_PACKAGE_MANAGER_CHECK) log_shakapacker_guard_warning( "Shakapacker::Utils::Manager does not define #{SHAKAPACKER_PACKAGE_MANAGER_CHECK}." ) nil end |
.install_shakapacker_package_manager_check_wrapper ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/react_on_rails/engine.rb', line 135 def self.install_shakapacker_package_manager_check_wrapper # Idempotency flag: skip re-patching the singleton on repeated calls (test reloads, # multi-app processes). Lives on Engine's singleton, so a constant reload resets it, # which is fine — the spec around block resets it explicitly during isolated tests. return if @shakapacker_guard_suppressed manager = shakapacker_utils_manager return unless manager original_package_manager_check = fetch_shakapacker_package_manager_guard_method(manager) return unless original_package_manager_check Rails.logger&.info( "[React on Rails] No Shakapacker config found; skipping Shakapacker " \ "packageManager check (Shakapacker is loaded as a transitive dependency but " \ "is not the configured bundler)." ) # Define the override on the singleton (not the class) so that any subsequent reopening # of Shakapacker::Utils::Manager that redefines the class method is still shadowed by # this singleton method, which Ruby resolves first. manager.define_singleton_method(SHAKAPACKER_PACKAGE_MANAGER_CHECK) do # Delegate back to the original guard once Shakapacker becomes the configured bundler. original_package_manager_check.call if ReactOnRails::Engine.shakapacker_configured_as_bundler? end @shakapacker_guard_suppressed = true end |
.log_shakapacker_guard_warning(message) ⇒ Object
195 196 197 198 199 200 |
# File 'lib/react_on_rails/engine.rb', line 195 def self.log_shakapacker_guard_warning() Rails.logger&.warn( "[React on Rails] #{} The packageManager guard suppression could not be applied. " \ "If boot fails, please report this at #{SHAKAPACKER_MANAGER_GUARD_ISSUE_URL}" ) end |
.package_json_missing? ⇒ Boolean
Check if package.json doesn’t exist yet
101 102 103 |
# File 'lib/react_on_rails/engine.rb', line 101 def self.package_json_missing? !File.exist?(VersionChecker::NodePackageVersion.package_json_path) end |
.running_generator? ⇒ Boolean
Check if we’re running a Rails generator Heuristic: Rails::Generators is typically only defined during generator commands. It could be defined by test helpers or gems that require “rails/generators”, but this is a fallback behind the ENV check above.
95 96 97 |
# File 'lib/react_on_rails/engine.rb', line 95 def self.running_generator? defined?(Rails::Generators) end |
.shakapacker_config_path ⇒ Object
Resolves the Shakapacker config path, mirroring how Shakapacker itself locates the file. Relative SHAKAPACKER_CONFIG values are expanded against Rails.root so bundler detection stays consistent with Shakapacker when the process starts from a different working directory.
116 117 118 119 120 121 |
# File 'lib/react_on_rails/engine.rb', line 116 def self.shakapacker_config_path env_config_path = ENV.fetch("SHAKAPACKER_CONFIG", nil) return Pathname.new(env_config_path).(Rails.root) unless env_config_path.to_s.empty? Rails.root.join("config", "shakapacker.yml") end |
.shakapacker_configured_as_bundler? ⇒ Boolean
Returns true when the host app has a Shakapacker config file, meaning Shakapacker is the configured bundler. Returns false when Shakapacker is only present as a transitive gem dependency (e.g., a Vite Rails app adopting React on Rails for client-only mounts).
109 110 111 |
# File 'lib/react_on_rails/engine.rb', line 109 def self.shakapacker_configured_as_bundler? shakapacker_config_path.exist? end |
.shakapacker_utils_manager ⇒ Object
179 180 181 182 183 184 |
# File 'lib/react_on_rails/engine.rb', line 179 def self.shakapacker_utils_manager return ::Shakapacker::Utils::Manager if defined?(::Shakapacker::Utils::Manager) log_shakapacker_guard_warning("Shakapacker is loaded but ::Shakapacker::Utils::Manager is not defined.") nil end |
.skip_version_validation? ⇒ Boolean
Thread Safety: ENV variables are process-global. In practice, Rails generators run in a single process, so concurrent execution is not a concern. If running generators concurrently (e.g., in parallel tests), ensure tests run in separate processes to avoid ENV variable conflicts.
Manual ENV Setting: While this ENV variable is designed to be set by generators, users can manually set it (e.g., ‘REACT_ON_RAILS_SKIP_VALIDATION=true rails server`) to bypass validation. This should only be done temporarily during debugging or setup scenarios. The validation helps catch version mismatches early, so bypassing it in production is not recommended.
Determine if version validation should be skipped
This method checks multiple conditions to determine if package version validation should be skipped. Validation is skipped during setup scenarios where the npm package isn’t installed yet (e.g., during generator execution).
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/react_on_rails/engine.rb', line 63 def self.skip_version_validation? # Skip if explicitly disabled via environment variable (set by generators) # Using ENV variable instead of ARGV because Rails can modify/clear ARGV during # initialization, making ARGV unreliable for detecting generator context. The ENV # variable persists through the entire Rails initialization process. if ENV["REACT_ON_RAILS_SKIP_VALIDATION"] == "true" Rails.logger.debug "[React on Rails] Skipping validation - disabled via environment variable" return true end # Check package.json first as it's cheaper and handles more cases if package_json_missing? Rails.logger.debug "[React on Rails] Skipping validation - package.json not found" return true end # Skip during generator runtime since packages are installed during execution # This is a fallback check in case ENV wasn't set, though ENV is the primary mechanism if running_generator? Rails.logger.debug "[React on Rails] Skipping validation during generator runtime" return true end false end |
.suppress_shakapacker_package_manager_check_if_not_bundler! ⇒ Object
Wrap Shakapacker’s package-manager guard when Shakapacker is not the bundler. The wrapper checks Shakapacker config presence at call time so the original guard still runs if another app in the same process configures Shakapacker later.
126 127 128 129 130 131 132 133 |
# File 'lib/react_on_rails/engine.rb', line 126 def self.suppress_shakapacker_package_manager_check_if_not_bundler! # Nothing to suppress when Shakapacker is genuinely absent from the load path. return unless defined?(::Shakapacker) return if shakapacker_configured_as_bundler? warn_if_shakapacker_env_config_missing install_shakapacker_package_manager_check_wrapper end |
.warn_if_shakapacker_env_config_missing ⇒ Object
Warn at boot when SHAKAPACKER_CONFIG points to a missing file so typos or not-yet-created paths surface clearly. The warning describes the config state (missing file at the user-specified path) rather than the suppression outcome, because the downstream install step may still bail out — keeping the message honest in that edge case.
168 169 170 171 172 173 174 175 176 177 |
# File 'lib/react_on_rails/engine.rb', line 168 def self.warn_if_shakapacker_env_config_missing env_config_path = ENV.fetch("SHAKAPACKER_CONFIG", nil) return if env_config_path.to_s.empty? Rails.logger&.warn( "[React on Rails] SHAKAPACKER_CONFIG is set to '#{env_config_path}' but the file " \ "does not exist. Bundler detection treated Shakapacker as not configured for this app; " \ "fix the path or unset the variable if this is unintended." ) end |