diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbaafbd4d..1dc964af1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,10 @@ Changes since the last non-beta release. - **`generated_component_packs_loading_strategy` now defaults based on Pro license**: When using Shakapacker >= 8.2.0, the default loading strategy is now `:async` for Pro users and `:defer` for non-Pro users. This provides optimal performance for Pro users while maintaining compatibility for non-Pro users. You can still explicitly set the strategy in your configuration. [PR #1993](https://github.com/shakacode/react_on_rails/pull/1993) by [AbanoubGhadban](https://github.com/AbanoubGhadban). +#### Documentation + +- **Simplified Configuration Files**: Improved configuration documentation and generator template for better clarity and usability. Reduced generator template from 67 to 42 lines (37% reduction). Added comprehensive testing configuration guide. Reorganized configuration docs into Essential vs Advanced sections. Enhanced Doctor program with diagnostics for server rendering and test compilation consistency. [PR #2011](https://github.com/shakacode/react_on_rails/pull/2011) by [justin808](https://github.com/justin808). + #### Deprecated - **Node Renderer Configuration**: Renamed `bundlePath` configuration option to `serverBundleCachePath` in the node renderer to better describe its purpose and avoid confusion with Shakapacker's public bundle path. The old `bundlePath` option continues to work with deprecation warnings. Both `RENDERER_SERVER_BUNDLE_CACHE_PATH` (new) and `RENDERER_BUNDLE_PATH` (deprecated) environment variables are supported. [PR #2008](https://github.com/shakacode/react_on_rails/pull/2008) by [justin808](https://github.com/justin808). diff --git a/docs/api-reference/configuration-deprecated.md b/docs/api-reference/configuration-deprecated.md new file mode 100644 index 0000000000..2afccb25c6 --- /dev/null +++ b/docs/api-reference/configuration-deprecated.md @@ -0,0 +1,52 @@ +# Deprecated Configuration Options + +This document lists configuration options that have been deprecated or removed from React on Rails. + +For current configuration options, see [configuration.md](configuration.md). + +## Removed Options + +### immediate_hydration + +**Status:** โš ๏ธ REMOVED in v17.0 + +This configuration option has been removed. Immediate hydration is now automatically enabled for Pro users and disabled for non-Pro users. + +**Migration:** Remove any `config.immediate_hydration` lines from your configuration. Use per-component overrides if needed: + +```ruby +# Pro users can disable for specific components: +react_component("MyComponent", immediate_hydration: false) + +# Non-Pro users: immediate_hydration is ignored +``` + +See [CHANGELOG.md](../../CHANGELOG.md) for details. + +## Deprecated Options + +### defer_generated_component_packs + +**Type:** Boolean +**Default:** `false` +**Status:** โš ๏ธ DEPRECATED + +**Renamed to:** `generated_component_packs_loading_strategy = :defer` + +**Migration:** + +```ruby +# Old (deprecated): +config.defer_generated_component_packs = true + +# New: +config.generated_component_packs_loading_strategy = :defer +``` + +See [CHANGELOG.md](../../CHANGELOG.md) for more details. + +## Need Help? + +- **Documentation:** [React on Rails Guides](https://www.shakacode.com/react-on-rails/docs/) +- **Support:** [ShakaCode Forum](https://forum.shakacode.com/) +- **Consulting:** [justin@shakacode.com](mailto:justin@shakacode.com) diff --git a/docs/api-reference/configuration-pro.md b/docs/api-reference/configuration-pro.md new file mode 100644 index 0000000000..583adb021d --- /dev/null +++ b/docs/api-reference/configuration-pro.md @@ -0,0 +1,42 @@ +# React on Rails Pro Configuration Options + +This document describes configuration options specific to React on Rails Pro features. + +For general React on Rails configuration options, see [configuration.md](configuration.md). + +## React Server Components (RSC) + +React Server Components and Streaming SSR are React on Rails Pro features. + +For detailed configuration of RSC and streaming features, see the Pro package documentation: +[react_on_rails_pro/docs/configuration.md](https://github.com/shakacode/react_on_rails/blob/master/react_on_rails_pro/docs/configuration.md) + +> **Note:** The Pro documentation is currently maintained separately in the `react_on_rails_pro` directory. We plan to migrate and consolidate Pro documentation into the main docs structure in a future PR for better discoverability and consistency. + +### Key Pro Configurations + +These options are configured in the `ReactOnRailsPro.configure` block: + +- `rsc_bundle_js_file` - Path to RSC bundle +- `react_client_manifest_file` - Client component manifest for RSC +- `react_server_client_manifest_file` - Server manifest for RSC +- `enable_rsc_support` - Enable React Server Components + +### Example Configuration + +```ruby +# config/initializers/react_on_rails_pro.rb +ReactOnRailsPro.configure do |config| + config.rsc_bundle_js_file = "rsc-bundle.js" + config.react_client_manifest_file = "client-manifest.json" + config.react_server_client_manifest_file = "server-manifest.json" + config.enable_rsc_support = true +end +``` + +See the Pro documentation for complete setup instructions. + +## Need Help? + +- **Pro Features:** [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/) +- **Consulting:** [justin@shakacode.com](mailto:justin@shakacode.com) diff --git a/docs/api-reference/configuration.md b/docs/api-reference/configuration.md index 96391204da..19e6fd9549 100644 --- a/docs/api-reference/configuration.md +++ b/docs/api-reference/configuration.md @@ -1,6 +1,16 @@ # React on Rails Configuration Options -Here is the full set of config options. This file is `/config/initializers/react_on_rails.rb` +This document describes all configuration options for React on Rails. Configuration is done in `/config/initializers/react_on_rails.rb`. + +> **๐Ÿ’ก Good News!** Most applications only need 2-3 configuration options. React on Rails provides sensible defaults for everything else. + +## Quick Start + +See [Essential Configuration](#essential-configuration) below for the minimal configuration options you'll commonly use. Most applications only need 1-2 settings! + +## Prerequisites + +### `/config/shakapacker.yml` First, you should have a `/config/shakapacker.yml` setup. @@ -39,346 +49,657 @@ production: cache_manifest: true ``` -Here's a representative `/config/initializers/react_on_rails.rb` setup when using this `/client` directory -for all client files, including your sources and node_modules. +## Configuration Categories + +React on Rails configuration options are organized into two categories: + +### Essential Configuration + +Options you'll commonly configure for most applications: + +- `server_bundle_js_file` - Server rendering bundle (recommended) +- `build_test_command` - Test environment build command (used with `ReactOnRails::TestHelper.configure_rspec_to_compile_assets`) + +### Advanced Configuration + +Options with sensible defaults that rarely need changing: + +- Component loading strategies (auto-configured based on Pro license) +- Server bundle security and organization +- I18n configuration +- Server rendering pool settings +- Custom rendering extensions +- And more... + +See sections below for complete documentation of all options. + +## Essential Configuration + +Here's a representative `/config/initializers/react_on_rails.rb` setup for essential options: ```ruby # frozen_string_literal: true -# NOTE: you typically will leave the commented out configurations set to their defaults. -# Thus, you only need to pay careful attention to the non-commented settings in this file. ReactOnRails.configure do |config| - # You can optionally add values to your rails_context. This object is passed - # every time a component renders. - # See below for an example definition of RenderingExtension - config.rendering_extension = RenderingExtension - - # `trace`: General debugging flag. - # The default is true for development, off otherwise. - # With true, you get detailed logs of rendering and stack traces if you call setTimout, - # setInterval, clearTimout when server rendering. - config.trace = Rails.env.development? # default - - # Configure if default DOM IDs have a random value or are fixed. - # false ==> Sets the dom id to "#{react_component_name}-react-component" - # true ==> Adds "-#{SecureRandom.uuid}" to that ID - # If you might use multiple instances of the same React component on a Rails page, then - # it is convenient to set this to true or else you have to either manually set the ids to - # avoid collisions. Most newer apps will have only one instance of a component on a page, - # so this should be false in most cases. - # This value can be overridden for a given call to react_component - config.random_dom_id = true # default - - # defaults to "" (top level) - config.node_modules_location = "" # If using Shakapacker you should use "". - - # If you're using the standard Shakapacker configuration of Webpack, then Shakapacker - # will automatically modify or create an assets:precompile task to build your assets. If so, - # set this value to nil. Alternatively, you can specify `config.build_production_command` - # to have react_on_rails invoke a command for you during assets:precompile. - # The command is either a script or a module containing a class method `call` - # In this example, the module BuildProductionCommand would have a class method `call`. - config.build_production_command = "RAILS_ENV=production bin/shakapacker" - - # NOTE: - # When specifying `build_production_command`, you need to disable `rails/shakapacker` - # configuration by setting `shakapacker_precompile: false` in your `config/shakapacker.yml` file. - - # See bottom for an example of the BuildProductionCommand module. - # config.build_production_command = BuildProductionCommand - # If you wish to utilize ReactOnRailsPro production bundle caching logic, then use - # config.build_production_command = ReactOnRailsPro::AssetsPrecompile - # and be sure to check ReactOnRailsPro's configuration documentation! - - ################################################################################ ################################################################################ - # SERVER RENDERING OPTIONS + # Server Rendering (Recommended) ################################################################################ - # This is the file used for server rendering of React when using `(prerender: true)` - # If you are not using server rendering, you should set this to `nil`. - # Note, there is only one server bundle, unlike JavaScript where you want to minimize the size - # of the JS sent to the client. For the server rendering, React on Rails creates a pool of - # JavaScript execution instances which should handle any component requested. - # - # While you may configure this to be the same as your client bundle file, this file is typically - # different. Note, be sure to include the exact file name with the ".js" if you are not hashing this file. - # If you are hashing this file (supposing you are using the same file for client rendering), then - # you should include a name that matches your bundle name in your Webpack config. + # This is the file used for server rendering of React when using `prerender: true` + # Set to "" if you are not using server rendering config.server_bundle_js_file = "server-bundle.js" ################################################################################ - # REACT SERVER COMPONENTS (RSC) AND STREAMING CONFIGURATION + # Test Configuration + # Used with ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + # This controls what command is run to build assets during tests ################################################################################ - # - # React Server Components and Streaming SSR are React on Rails Pro features. - # For detailed configuration of RSC and streaming features, see: - # https://github.com/shakacode/react_on_rails/blob/master/react_on_rails_pro/docs/configuration.md - # - # Key Pro configurations (configured in ReactOnRailsPro.configure block): - # - rsc_bundle_js_file: Path to RSC bundle - # - react_client_manifest_file: Client component manifest for RSC - # - react_server_client_manifest_file: Server manifest for RSC - # - enable_rsc_support: Enable React Server Components - # - # See Pro documentation for complete setup instructions. + config.build_test_command = "RAILS_ENV=test bin/shakapacker" +end +``` - ################################################################################ - # SERVER BUNDLE SECURITY AND ORGANIZATION - ################################################################################ +### server_bundle_js_file - # This configures the directory (relative to the Rails root) where the server bundle will be output. - # By default, this is "ssr-generated". If set to nil, the server bundle will be loaded from the same - # public directory as client bundles. For enhanced security, use this option in conjunction with - # `enforce_private_server_bundles` to ensure server bundles are only loaded from private directories - # config.server_bundle_output_path = "ssr-generated" +**Type:** String +**Default:** `""` +**Required for:** Server-side rendering - # When set to true, React on Rails will only load server bundles from private, explicitly configured directories (such as `ssr-generated`), and will raise an error if a server bundle is found in a public or untrusted location. This helps prevent accidental or malicious execution of untrusted JavaScript on the server, and is strongly recommended for production environments. And prevent leakage of server-side code to the client (Especially in the case of RSC). - # Default is false for backward compatibility, but enabling this option is a best practice for security. - config.enforce_private_server_bundles = false +The filename of your server bundle used for server-side rendering with `prerender: true`. - ################################################################################ - # BUNDLE ORGANIZATION EXAMPLES - ################################################################################ - # - # This configuration creates a clear separation between client and server assets: - # - # CLIENT BUNDLES (Public, Web-Accessible): - # Location: public/webpack/[environment]/ or public/packs/ (According to your shakapacker.yml configuration) - # Files: application.js, manifest.json, CSS files - # Served by: Web server directly - # Access: ReactOnRails::Utils.public_bundles_full_path - # - # SERVER BUNDLES (Private, Server-Only): - # Location: ssr-generated/ (when server_bundle_output_path configured) - # Files: server-bundle.js, rsc-bundle.js - # Served by: Never served to browsers - # Access: ReactOnRails::Utils.server_bundle_js_file_path - # - # Example directory structure with recommended configuration: - # app/ - # โ”œโ”€โ”€ ssr-generated/ # Private server bundles - # โ”‚ โ”œโ”€โ”€ server-bundle.js - # โ”‚ โ””โ”€โ”€ rsc-bundle.js - # โ””โ”€โ”€ public/ - # โ””โ”€โ”€ webpack/development/ # Public client bundles - # โ”œโ”€โ”€ application.js - # โ”œโ”€โ”€ manifest.json - # โ””โ”€โ”€ styles.css - # - ################################################################################ +- Set to `"server-bundle.js"` if using server rendering +- Set to `""` if not using server rendering +- This file is used by React on Rails' JavaScript execution pool for server rendering - # `prerender` means server-side rendering - # default is false. This is an option for view helpers `render_component` and `render_component_hash`. - # Set to true to change the default value to true. - config.prerender = false - - # THE BELOW OPTIONS FOR SERVER-SIDE RENDERING RARELY NEED CHANGING - # - # This value only affects server-side rendering when using the webpack-dev-server - # If you are hashing the server bundle and you want to use the same bundle for client and server, - # you'd set this to `true` so that React on Rails reads the server bundle from the webpack-dev-server. - # Normally, you have different bundles for client and server, thus, the default is false. - # Furthermore, if you are not hashing the server bundle (not in the manifest.json), then React on Rails - # will only look for the server bundle to be created in the typical file location, typically by - # a `shakapacker --watch` process. - # If true, ensure that in config/shakapacker.yml that you have both dev_server.hmr and - # dev_server.inline set to false. - config.same_bundle_for_client_and_server = false - - # If set to true, this forces Rails to reload the server bundle if it is modified - # Default value is Rails.env.development? - # You probably will never change this. - config.development_mode = Rails.env.development? - - # For server rendering so that the server-side console replays in the browser console. - # This can be set to false so that server side messages are not displayed in the browser. - # Default is true. Be cautious about turning this off, as it can make debugging difficult. - # Default value is true - config.replay_console = true - - # Default is true. Logs server rendering messages to Rails.logger.info. If false, you'll only - # see the server rendering messages in the browser console. - config.logging_on_server = true - - # Default is true only for development? to raise exception on server if the JS code throws for - # server rendering. The reason is that the server logs will show the error and force you to fix - # any server rendering issues immediately during development. - config.raise_on_prerender_error = Rails.env.development? - - # This configuration allows logic to be applied to client rendered props, such as stripping props that are only used during server rendering. - # Add a module with an adjust_props_for_client_side_hydration method that expects the component's name & props hash - # See below for an example definition of RenderingPropsExtension - config.rendering_props_extension = RenderingPropsExtension +Note: There should be ONE server bundle that can render all your server-rendered components, unlike client bundles where you minimize size. - ################################################################################ - # Server Renderer Configuration for ExecJS - ################################################################################ - # The default server rendering is ExecJS, by default using Node.js runtime - # If you wish to use an alternative Node server rendering for higher performance, - # contact justin@shakacode.com for details. - # - # For ExecJS: - # You can configure your pool of JS virtual machines and specify where it should load code: - # On MRI, use `node.js` runtime for the best performance - # (see https://github.com/shakacode/react_on_rails/issues/1438) - # Also see https://github.com/shakacode/react_on_rails/issues/1457#issuecomment-1165026717 if using `mini_racer` - # On MRI, you'll get a deadlock with `pool_size` > 1 - # If you're using JRuby, you can increase `pool_size` to have real multi-threaded rendering. - config.server_renderer_pool_size = 1 # increase if you're on JRuby - config.server_renderer_timeout = 20 # seconds +### build_test_command - ################################################################################ - ################################################################################ - # FILE SYSTEM BASED COMPONENT REGISTRY - # `render_component` and `render_component_hash` view helper methods can - # auto-load the bundle for the generated component, to avoid having to specify the - # bundle manually for each view with the component. - # - # SHAKAPACKER VERSION REQUIREMENTS: - # - Basic pack generation: Shakapacker 6.5.1+ - # - Advanced auto-registration with nested entries: Shakapacker 7.0.0+ - # - Async loading support: Shakapacker 8.2.0+ - # - # Feature Compatibility Matrix: - # | Shakapacker Version | Basic Pack Generation | Auto-Registration | Nested Entries | Async Loading | - # |-------------------|----------------------|-------------------|----------------|---------------| - # | 6.5.1 - 6.9.x | โœ… Yes | โŒ No | โŒ No | โŒ No | - # | 7.0.0 - 8.1.x | โœ… Yes | โœ… Yes | โœ… Yes | โŒ No | - # | 8.2.0+ | โœ… Yes | โœ… Yes | โœ… Yes | โœ… Yes | - # - ################################################################################ - # components_subdirectory is the name of the subdirectory matched to detect and register components automatically - # The default is nil. You can enable the feature by updating it in the next line. - config.components_subdirectory = nil - # Change to a value like this example to enable this feature - # config.components_subdirectory = "ror_components" - - # Default is false. - # The default can be overridden as an option in calls to view helpers - # `render_component` and `render_component_hash`. You may set to true to change the default to auto loading. - # NOTE: Requires Shakapacker 6.5.1+ for basic functionality, 7.0.0+ for full auto-registration features. - # See version requirements matrix above for complete feature compatibility. - config.auto_load_bundle = false - - # Default is false - # Set this to true & instead of trying to import the generated server components into your existing - # server bundle entrypoint, the PacksGenerator will create a server bundle entrypoint using - # config.server_bundle_js_file for the filename. - config.make_generated_server_bundle_the_entrypoint = false - - # Configuration for how generated component packs are loaded. - # Options: :sync, :async, :defer - # - :sync (default for Shakapacker < 8.2.0): Loads scripts synchronously - # - :async (default for Shakapacker โ‰ฅ 8.2.0): Loads scripts asynchronously for better performance - # - :defer: Defers script execution until after page load - config.generated_component_packs_loading_strategy = :async - - # DEPRECATED: Use `generated_component_packs_loading_strategy` instead. - # Migration: `defer_generated_component_packs: true` โ†’ `generated_component_packs_loading_strategy: :defer` - # Migration: `defer_generated_component_packs: false` โ†’ `generated_component_packs_loading_strategy: :sync` - # See [16.0.0 Release Notes](docs/release-notes/16.0.0.md) for more details. - # config.defer_generated_component_packs = false - - # Default is false - # React on Rails Pro (licensed) feature: When true, components hydrate immediately as soon as - # their server-rendered HTML reaches the client, without waiting for the full page load. - # This improves time-to-interactive performance. - config.immediate_hydration = false +**Type:** String +**Default:** `""` +**Used with:** `ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)` + +**Important:** This option is only needed if you're using the React on Rails test helper. The two approaches below are **mutually exclusive** - use one or the other, not both. + +#### Recommended Approach: Shakapacker Auto-Compilation + +Set `compile: true` in `config/shakapacker.yml` for the test environment. Shakapacker will automatically compile assets before running tests: + +```yaml +test: + compile: true + public_output_path: webpack/test +``` + +**Pros:** + +- Simpler configuration (no extra setup in spec helpers) +- Managed by Shakapacker directly +- Automatically integrates with Rails test environment + +**Cons:** + +- Less explicit control over when compilation happens +- May compile more often than necessary + +#### Alternative Approach: React on Rails Test Helper + +Use `build_test_command` with `ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)` if you need explicit control: + +```ruby +# config/initializers/react_on_rails.rb +config.build_test_command = "RAILS_ENV=test bin/shakapacker" +``` + +```ruby +# spec/rails_helper.rb (or spec_helper.rb) +require "react_on_rails/test_helper" + +RSpec.configure do |config| + ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) +end +``` + +**Pros:** + +- Explicit control over compilation timing +- Only compiles once before test suite runs +- Can customize the build command + +**Cons:** + +- Requires additional setup in spec helpers +- More configuration to maintain + +For more details on testing configuration, see the [Testing Configuration Guide](../guides/testing-configuration.md). + +## File-Based Component Registry + +If you have many components and want to avoid manually managing webpack entry points for each one, React on Rails can automatically generate component packs based on your file system structure. This feature is particularly useful for large applications with dozens of components. + +For complete information about the file-based component registry feature (including `components_subdirectory`, `auto_load_bundle`, and `make_generated_server_bundle_the_entrypoint` configuration options), see: + +[Auto-Bundling: File-System-Based Automated Bundle Generation](../core-concepts/auto-bundling-file-system-based-automated-bundle-generation.md) + +## Advanced Configuration + +The following sections document advanced configuration options. Most applications won't need to change these as they have sensible defaults. + +### Component Loading Strategy + +#### generated_component_packs_loading_strategy + +**Type:** Symbol (`:async`, `:defer`, or `:sync`) +**Default:** `:async` for Pro users with Shakapacker 8.2.0+, `:defer` for non-Pro users, `:sync` for older Shakapacker +**Auto-configured:** โœ… Yes + +Controls how generated component pack scripts are loaded: + +- `:async` - Loads scripts asynchronously (Pro users, best performance) +- `:defer` - Defers script execution until after page load (non-Pro users) +- `:sync` - Loads scripts synchronously (fallback for Shakapacker < 8.2.0) + +**You typically don't need to set this** - React on Rails automatically selects the best strategy based on your Pro license status and Shakapacker version. + +**When to override:** Only change this if you have specific performance requirements or constraints. For example, you might use `:defer` if you need to ensure all page content loads before scripts execute, or `:sync` for testing purposes. + +```ruby +config.generated_component_packs_loading_strategy = :defer +``` + +### Server Bundle Security and Organization + +#### server_bundle_output_path + +**Type:** String or nil +**Default:** `"ssr-generated"` + +> โš ๏ธ **DO NOT change this setting unless you have a specific reason.** The default is correct for virtually all applications. + +Directory (relative to Rails root) where server bundles are output. + +```ruby +# No need to set this - the default is recommended +# config.server_bundle_output_path = "ssr-generated" +``` + +- When set to a string: Server bundles output to this directory (e.g., `ssr-generated/`) +- When `nil`: Server bundles loaded from same public directory as client bundles (not recommended) + +The default `"ssr-generated"` keeps server bundles separate from public assets for security. + +#### enforce_private_server_bundles + +**Type:** Boolean +**Default:** `false` +**Recommended for production:** `true` + +When true, React on Rails only loads server bundles from private directories (configured via `server_bundle_output_path`), preventing accidental exposure of server code: + +```ruby +config.enforce_private_server_bundles = true +``` + +Benefits: + +- Prevents server-side code from being web-accessible +- Protects against malicious JavaScript execution +- Especially important for React Server Components + +### Production Build + +#### build_production_command + +**Type:** String or Module +**Default:** `nil` +**Typical usage:** Only if customizing asset compilation + +Command to run during `assets:precompile` to build production assets: + +```ruby +config.build_production_command = "RAILS_ENV=production bin/shakapacker" +``` + +**Important:** When setting this, you must disable Shakapacker's precompile by setting `shakapacker_precompile: false` in `config/shakapacker.yml`. + +**Most apps don't need this** - Shakapacker handles asset compilation automatically. + +### Common Configuration + +These are commonly used configuration options that many applications will need: + +#### rendering_extension + +**Type:** Module +**Default:** `nil` + +Module that adds custom values to the `railsContext` object passed to all components: + +```ruby +module RenderingExtension + def self.custom_context(view_context) + { + somethingUseful: view_context.session[:something_useful], + currentUser: view_context.current_user&.as_json + } + end +end + +config.rendering_extension = RenderingExtension +``` + +#### rendering_props_extension + +**Type:** Module +**Default:** `nil` + +Module that modifies props for client-side hydration (useful for stripping server-only props): + +```ruby +module RenderingPropsExtension + def self.adjust_props_for_client_side_hydration(component_name, props) + component_name == 'HelloWorld' ? props.except(:server_side_only) : props + end +end + +config.rendering_props_extension = RenderingPropsExtension +``` + +### Server Rendering Options + +#### prerender + +**Type:** Boolean +**Default:** `false` + +Global default for server-side rendering. When true, all `react_component` calls will server render by default. + +**Most apps prefer to set this at the `react_component` call level** rather than globally: + +```ruby +# Preferred: Set per-component +react_component("MyComponent", prerender: true) +``` + +To set a global default: + +```ruby +config.prerender = true # Server render all components by default +``` + +You can override the global setting per-component: + +```ruby +react_component("MyComponent", prerender: false) # Skip SSR for this component +``` + +### Development and Debugging + +#### trace + +**Type:** Boolean +**Default:** `Rails.env.development?` +**Auto-configured:** โœ… Yes + +Enables detailed logging for server rendering, including stack traces for setTimeout/setInterval calls: + +```ruby +config.trace = Rails.env.development? # default +``` + +#### development_mode + +**Type:** Boolean +**Default:** `Rails.env.development?` +**Auto-configured:** โœ… Yes + +Forces Rails to reload server bundle when modified: + +```ruby +config.development_mode = Rails.env.development? # default +``` + +#### replay_console +**Type:** Boolean +**Default:** `true` + +When true, server-side console messages replay in the browser console. This is valuable for debugging server-rendering issues. + +```ruby +config.replay_console = true # default +``` + +**When to disable:** You might set this to `false` in production if console logs contain sensitive data or to reduce client-side payload size. + +#### logging_on_server + +**Type:** Boolean +**Default:** `true` + +Logs server rendering messages to `Rails.logger.info`: + +```ruby +config.logging_on_server = true # default +``` + +**Pro Node Renderer Note:** When using the Pro Node Renderer, you might set this to `false` to avoid duplication of logs, as the Node Renderer handles its own logging. + +#### raise_on_prerender_error + +**Type:** Boolean +**Default:** `Rails.env.development?` +**Auto-configured:** โœ… Yes + +Raises exceptions when JavaScript errors occur during server rendering (development only by default): + +```ruby +config.raise_on_prerender_error = Rails.env.development? # default +``` + +### Server Renderer Pool (ExecJS) + +#### server_renderer_pool_size + +**Type:** Integer +**Default:** `1` (or environment-based) +**Auto-configured:** โœ… Yes + +Number of JavaScript execution instances in the server rendering pool: + +```ruby +config.server_renderer_pool_size = 1 # MRI default (avoid deadlock) +config.server_renderer_pool_size = 5 # JRuby (can handle multi-threading) +``` + +**MRI users:** Keep at 1 to avoid deadlocks +**JRuby users:** Can increase for multi-threaded rendering + +#### server_renderer_timeout + +**Type:** Integer (seconds) +**Default:** `20` + +Maximum time to wait for server rendering to complete: + +```ruby +config.server_renderer_timeout = 20 # default +``` + +### Component DOM IDs + +#### random_dom_id + +**Type:** Boolean +**Default:** `true` + +Controls whether component DOM IDs include a random UUID: + +- `true` - IDs like `MyComponent-react-component-a1b2c3d4` +- `false` - IDs like `MyComponent-react-component` + +```ruby +config.random_dom_id = false # Use fixed IDs +``` + +**When to use false:** Modern apps typically have one component instance per page. +**When to use true:** Multiple instances of same component on one page. + +Can be overridden per-component: + +```ruby +react_component("MyComponent", random_dom_id: false) +``` + +### Component Registry Timeout + +#### component_registry_timeout + +**Type:** Integer (milliseconds) +**Default:** `5000` + +Maximum time to wait for client-side component registration after page load: + +```ruby +config.component_registry_timeout = 5000 # default (5 seconds) +``` + +Set to `0` to wait indefinitely (not recommended for production). + +### I18n Configuration + +These options are for applications using [react-intl](https://formatjs.io/docs/react-intl/) or similar internationalization libraries. If your application doesn't need i18n, you can skip this section. + +#### i18n_dir + +**Type:** String or nil +**Default:** `nil` + +Directory where i18n translation files are output for use by react-intl: + +```ruby +config.i18n_dir = Rails.root.join("client", "app", "libs", "i18n") +``` + +Set to `nil` to disable i18n features. + +#### i18n_yml_dir + +**Type:** String +**Default:** `Rails.root.join("config", "locales")` + +Directory where i18n YAML source files are located: + +```ruby +config.i18n_yml_dir = Rails.root.join("config", "locales") +``` + +#### i18n_output_format + +**Type:** String +**Default:** `'json'` + +Format for generated i18n files (`'json'` or `'js'`): + +```ruby +config.i18n_output_format = 'json' # default +``` + +#### i18n_yml_safe_load_options + +**Type:** Hash +**Default:** `{}` + +Options passed to `YAML.safe_load` when reading locale files: + +```ruby +config.i18n_yml_safe_load_options = { permitted_classes: [Symbol] } +``` + +### Webpack Integration + +#### webpack_generated_files + +**Type:** Array of Strings +**Default:** `%w[manifest.json]` (auto-populated with server bundle files) + +Files that webpack generates, used by test helper to check if compilation is needed: + +```ruby +config.webpack_generated_files = %w[server-bundle.js manifest.json] +``` + +**Note:** Don't include hashed filenames (from manifest.json) as they change on every build. + +#### same_bundle_for_client_and_server + +**Type:** Boolean +**Default:** `false` + +When true, React on Rails reads the server bundle from webpack-dev-server (useful if using same hashed bundle for client and server): + +```ruby +config.same_bundle_for_client_and_server = false # default +``` + +**This should almost never be true.** Almost all apps should use separate client/server bundles. + +When true, also set in `config/shakapacker.yml`: + +```yaml +dev_server: + hmr: false + inline: false +``` + +### Internal Options + +#### node_modules_location + +**Type:** String +**Default:** `Rails.root` + +Location of `node_modules` directory. With Shakapacker, this should typically be `""` (project root): + +```ruby +config.node_modules_location = "" # Shakapacker default +``` + +#### server_render_method + +**Type:** String +**Default:** `nil` + +Server rendering method. Only `"ExecJS"` is currently supported: + +```ruby +config.server_render_method = nil # Uses ExecJS +``` + +For alternative server rendering methods, contact [justin@shakacode.com](mailto:justin@shakacode.com). + +For deprecated configuration options, see [configuration-deprecated.md](configuration-deprecated.md). + +## Complete Example + +Here's a complete example showing commonly changed options: + +```ruby +# frozen_string_literal: true + +ReactOnRails.configure do |config| ################################################################################ - # I18N OPTIONS + # Essential Configuration ################################################################################ - # Replace the following line to the location where you keep translation.js & default.js for use - # by the npm packages react-intl. Be sure this directory exists! - # config.i18n_dir = Rails.root.join("client", "app", "libs", "i18n") - # - # If not using the i18n feature, then leave this section commented out or set the value - # of config.i18n_dir to nil. - # - # Replace the following line to the location where you keep your client i18n yml files - # that will source for automatic generation on translations.js & default.js - # By default(without this option) all yaml files from Rails.root.join("config", "locales") - # and installed gems are loaded - config.i18n_yml_dir = Rails.root.join("config", "locales") - - # Possible output formats are js and json - # The default format is json - config.i18n_output_format = 'json' - - # Possible YAML.safe_load options pass-through for locales - # config.i18n_yml_safe_load_options = { permitted_classes: [Symbol] } + + # Server rendering bundle + config.server_bundle_js_file = "server-bundle.js" + + # Test configuration + config.build_test_command = "RAILS_ENV=test bin/shakapacker" + + # File-based component registry + config.components_subdirectory = "ror_components" + config.auto_load_bundle = true ################################################################################ - ################################################################################ - # TEST CONFIGURATION OPTIONS - # Below options are used with the use of this test helper: - # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - # - # NOTE: - # Instead of using this test helper, you may ensure fresh test files using Shakapacker via: - # 1. Have `config/webpack/test.js` exporting an array of objects to configure both client and server bundles. - # 2. Set the compile option to true in config/shakapacker.yml for env test + # Optional Overrides (most apps don't need these) ################################################################################ - # If you are using this in your spec_helper.rb (or rails_helper.rb): - # - # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - # - # with rspec then this controls what yarn command is run - # to automatically refresh your Webpack assets on every test run. - # - config.build_test_command = "RAILS_ENV=test bin/shakapacker" + # Production build (only if not using standard Shakapacker) + # config.build_production_command = "RAILS_ENV=production bin/shakapacker" + + # Server bundle security (recommended for production) + # config.enforce_private_server_bundles = true - # CONFIGURE YOUR SOURCE FILES - # The test helper needs to know where your JavaScript files exist. The value is configured - # by your config/shakapacker.yml source_path: - # source_path: client/app # if using recommended /client directory - # - # Define the files we need to check for Webpack compilation when running tests. - # The default is `%w( manifest.json )` as will be sufficient for most shakapacker builds. - # However, if you are generating a server bundle that is NOT hashed (present in manifest.json), - # then include the file in this list like this: - config.webpack_generated_files = %w( server-bundle.js manifest.json ) - # Note, be sure NOT to include your server-bundle.js if it is hashed, or else React on Rails will - # think the server-bundle.js is missing every time for test runs. + # Custom rendering hooks + # config.rendering_extension = RenderingExtension + # config.rendering_props_extension = RenderingPropsExtension end ``` -Example of a ReactOnRailsConfig module for `client_props_extension`: +## Pro Features + +For React Server Components (RSC) and other Pro-specific configuration options, see: +[configuration-pro.md](configuration-pro.md) + +## Deprecated Options + +For deprecated and removed configuration options, see: +[configuration-deprecated.md](configuration-deprecated.md) + +## Support Examples + +### Custom Rendering Extension + +```ruby +module RenderingExtension + def self.custom_context(view_context) + if view_context.controller.is_a?(ActionMailer::Base) + {} + else + { + somethingUseful: view_context.session[:something_useful], + currentUser: view_context.current_user&.as_json + } + end + end +end +``` + +### Custom Props Extension ```ruby module RenderingPropsExtension - # The modify_props method will be called by ReactOnRails::ReactComponent::RenderOptions if config.client_props_extension is defined def self.adjust_props_for_client_side_hydration(component_name, props) - component_name == 'HelloWorld' ? props.except(:server_side_only) : props + # Strip server-only props before sending to client + props.except(:server_side_authentication_token, :internal_admin_data) end end ``` -Example of a ReactOnRailsConfig module for `production_build_command`: +### Custom Build Command Module ```ruby module BuildProductionCommand include FileUtils - # The call method will be called during assets:precompile + def self.call - sh "bin/shakapacker" + sh "RAILS_ENV=production NODE_ENV=production bin/shakapacker" + # Additional custom build steps here end end + +# In your config: +config.build_production_command = BuildProductionCommand ``` -Example of a RenderingExtension for custom values in the `rails_context`: +## Bundle Organization Example -```ruby -module RenderingExtension +Recommended directory structure with private server bundles: - # Return a Hash that contains custom values from the view context that will get merged with - # the standard rails_context values and passed to all calls to Render-Functions used by the - # react_component and redux_store view helpers - def self.custom_context(view_context) - { - somethingUseful: view_context.session[:something_useful] - } - end -end +```text +app/ +โ”œโ”€โ”€ ssr-generated/ # Private server bundles (never served to browsers) +โ”‚ โ”œโ”€โ”€ server-bundle.js +โ”‚ โ””โ”€โ”€ rsc-bundle.js +โ””โ”€โ”€ public/ + โ””โ”€โ”€ webpack/development/ # Public client bundles (web-accessible) + โ”œโ”€โ”€ application.js + โ”œโ”€โ”€ manifest.json + โ””โ”€โ”€ styles.css ``` + +Access methods: + +- **Client bundles:** `ReactOnRails::Utils.public_bundles_full_path` +- **Server bundles:** `ReactOnRails::Utils.server_bundle_js_file_path` + +## Need Help? + +- **Documentation:** [React on Rails Guides](https://www.shakacode.com/react-on-rails/docs/) +- **Pro Features:** [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/) +- **Support:** [ShakaCode Forum](https://forum.shakacode.com/) +- **Consulting:** [justin@shakacode.com](mailto:justin@shakacode.com) + +--- + +**Note:** This configuration file is meant to be a complete reference. For most applications, you'll only use a small subset of these options. Start with the [Essential Configuration](#essential-configuration) and add advanced options only as needed. diff --git a/docs/guides/testing-configuration.md b/docs/guides/testing-configuration.md new file mode 100644 index 0000000000..d4adea62bf --- /dev/null +++ b/docs/guides/testing-configuration.md @@ -0,0 +1,398 @@ +# Testing Configuration Guide + +This guide explains how to configure React on Rails for optimal testing with RSpec, Minitest, or other test frameworks. + +## Quick Start + +For most applications, the simplest approach is to let Shakapacker handle asset compilation automatically: + +```yaml +# config/shakapacker.yml +test: + compile: true + public_output_path: webpack/test +``` + +That's it! Shakapacker will automatically compile assets before running tests. + +## Two Approaches to Test Asset Compilation + +React on Rails supports two mutually exclusive approaches for compiling webpack assets during tests: + +### Approach 1: Shakapacker Auto-Compilation (Recommended) + +**Best for:** Most applications, especially simpler test setups + +**Configuration:** + +```yaml +# config/shakapacker.yml +test: + <<: *default + compile: true + public_output_path: webpack/test +``` + +**How it works:** + +- Shakapacker automatically compiles assets when they're requested +- No additional configuration in React on Rails or test helpers needed +- Assets are compiled on-demand during test runs + +**Pros:** + +- โœ… Simplest configuration +- โœ… No extra setup in spec helpers +- โœ… Automatically integrates with Rails test environment +- โœ… Works with any test framework (RSpec, Minitest, etc.) + +**Cons:** + +- โš ๏ธ May compile assets multiple times during test runs +- โš ๏ธ Less explicit control over when compilation happens +- โš ๏ธ Can slow down tests if assets change frequently + +**When to use:** + +- You want the simplest possible configuration +- Your test suite is relatively fast +- You don't mind automatic compilation on-demand + +### Approach 2: React on Rails Test Helper (Explicit Control) + +**Best for:** Applications needing precise control over compilation timing + +**Configuration:** + +```ruby +# config/initializers/react_on_rails.rb +ReactOnRails.configure do |config| + config.build_test_command = "RAILS_ENV=test bin/shakapacker" +end +``` + +```ruby +# spec/rails_helper.rb (for RSpec) +require "react_on_rails/test_helper" + +RSpec.configure do |config| + # Ensures webpack assets are compiled before the test suite runs + ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) +end +``` + +```ruby +# test/test_helper.rb (for Minitest) +require "react_on_rails/test_helper" + +class ActiveSupport::TestCase + # Ensures webpack assets are compiled before running tests + ReactOnRails::TestHelper.ensure_assets_compiled +end +``` + +**How it works:** + +- Compiles assets once before the test suite starts +- Uses the `build_test_command` configuration +- Fails fast if compilation has errors + +**Pros:** + +- โœ… Explicit control over compilation timing +- โœ… Assets compiled only once per test run +- โœ… Clear error messages if compilation fails +- โœ… Can customize the build command + +**Cons:** + +- โš ๏ธ Requires additional configuration in test helpers +- โš ๏ธ More setup to maintain +- โš ๏ธ Requires `build_test_command` to be set + +**When to use:** + +- You want to compile assets exactly once before tests +- You need to customize the build command +- You want explicit error handling for compilation failures +- Your test suite is slow and you want to optimize compilation + +## โš ๏ธ Important: Don't Mix Approaches + +**Do not use both approaches together.** They are mutually exclusive: + +โŒ **Wrong:** + +```yaml +# config/shakapacker.yml +test: + compile: true # โ† Don't do this... +``` + +```ruby +# config/initializers/react_on_rails.rb +config.build_test_command = "RAILS_ENV=test bin/shakapacker" # โ† ...with this + +# spec/rails_helper.rb +ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) # โ† ...and this +``` + +This will cause assets to be compiled multiple times unnecessarily. + +## Migrating Between Approaches + +### From React on Rails Test Helper โ†’ Shakapacker Auto-Compilation + +1. Set `compile: true` in `config/shakapacker.yml` test section: + + ```yaml + test: + compile: true + public_output_path: webpack/test + ``` + +2. Remove test helper configuration from spec/test helpers: + + ```ruby + # spec/rails_helper.rb - REMOVE these lines: + require "react_on_rails/test_helper" + ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + ``` + +3. Remove or comment out `build_test_command` in React on Rails config: + ```ruby + # config/initializers/react_on_rails.rb + # config.build_test_command = "RAILS_ENV=test bin/shakapacker" # โ† Comment out + ``` + +### From Shakapacker Auto-Compilation โ†’ React on Rails Test Helper + +1. Set `compile: false` in `config/shakapacker.yml` test section: + + ```yaml + test: + compile: false + public_output_path: webpack/test + ``` + +2. Add `build_test_command` to React on Rails config: + + ```ruby + # config/initializers/react_on_rails.rb + config.build_test_command = "RAILS_ENV=test bin/shakapacker" + ``` + +3. Add test helper configuration: + + ```ruby + # spec/rails_helper.rb (for RSpec) + require "react_on_rails/test_helper" + + RSpec.configure do |config| + ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + end + ``` + +## Verifying Your Configuration + +Use the React on Rails doctor command to verify your test configuration: + +```bash +bundle exec rake react_on_rails:doctor +``` + +The doctor will check: + +- Whether `compile: true` is set in shakapacker.yml +- Whether `build_test_command` is configured +- Whether test helpers are properly set up +- Whether you're accidentally using both approaches + +## Troubleshooting + +### Assets not compiling during tests + +**Problem:** Tests fail because JavaScript/CSS assets are not compiled. + +**Solution:** Check which approach you're using: + +1. **If using Shakapacker auto-compilation:** + + ```yaml + # config/shakapacker.yml + test: + compile: true # โ† Make sure this is true + ``` + +2. **If using React on Rails test helper:** + - Verify `build_test_command` is set + - Check that test helper is configured in spec/test helper + - Run `bundle exec rake react_on_rails:doctor` + +### Assets compiling multiple times + +**Problem:** Tests are slow because assets compile repeatedly. + +**Solutions:** + +1. **If using Shakapacker auto-compilation:** + + - Switch to React on Rails test helper for one-time compilation + - Or ensure `cache_manifest: true` in shakapacker.yml + +2. **If using React on Rails test helper:** + - This shouldn't happen - assets should compile only once + - Check that you don't also have `compile: true` in shakapacker.yml + +### Build command fails + +**Problem:** `build_test_command` fails with errors. + +**Check:** + +1. Does `bin/shakapacker` exist and is it executable? + + ```bash + ls -la bin/shakapacker + chmod +x bin/shakapacker # If needed + ``` + +2. Can you run the command manually? + + ```bash + RAILS_ENV=test bin/shakapacker + ``` + +3. Are your webpack configs valid for test environment? + +### Test helper not found + +**Problem:** `LoadError: cannot load such file -- react_on_rails/test_helper` + +**Solution:** Make sure react_on_rails gem is available in test environment: + +```ruby +# Gemfile +gem "react_on_rails", ">= 16.0" # Not in a specific group + +# Or explicitly in test group: +group :test do + gem "react_on_rails" +end +``` + +## Performance Considerations + +### Asset Compilation Speed + +**Shakapacker auto-compilation:** + +- Compiles on first request per test process +- May compile multiple times in parallel test environments +- Good for: Small test suites, simple webpack configs + +**React on Rails test helper:** + +- Compiles once before entire test suite +- Blocks test start until compilation complete +- Good for: Large test suites, complex webpack configs + +### Caching Strategies + +Improve compilation speed with caching: + +```yaml +# config/shakapacker.yml +test: + cache_manifest: true # Cache manifest between runs +``` + +```ruby +# config/initializers/react_on_rails.rb +# If using test helper, webpack will use its own caching +config.build_test_command = "RAILS_ENV=test bin/shakapacker" +``` + +### Parallel Testing + +When running tests in parallel (with `parallel_tests` gem): + +**Shakapacker auto-compilation:** + +- Each process compiles independently (may be slow) +- Consider precompiling assets before running parallel tests: + ```bash + RAILS_ENV=test bin/shakapacker + bundle exec rake parallel:spec + ``` + +**React on Rails test helper:** + +- Compiles once before forking processes (efficient) +- Works well out of the box with parallel testing + +## CI/CD Considerations + +### GitHub Actions / GitLab CI + +**Option 1: Precompile before tests** + +```yaml +- name: Compile test assets + run: RAILS_ENV=test bundle exec rake react_on_rails:assets:compile_environment + +- name: Run tests + run: bundle exec rspec +``` + +**Option 2: Use Shakapacker auto-compilation** + +```yaml +# config/shakapacker.yml +test: + compile: true + +# CI workflow +- name: Run tests (assets auto-compile) + run: bundle exec rspec +``` + +### Docker + +When running tests in Docker, consider: + +1. Caching `node_modules` between builds +2. Precompiling assets in Docker build stage +3. Using bind mounts for local development + +## Best Practices + +1. **Choose one approach** - Don't mix Shakapacker auto-compilation with React on Rails test helper +2. **Use doctor command** - Run `rake react_on_rails:doctor` to verify configuration +3. **Precompile in CI** - Consider precompiling assets before running tests in CI +4. **Cache node_modules** - Speed up installation with caching +5. **Monitor compile times** - If tests are slow, check asset compilation timing + +## Summary Decision Matrix + +| Scenario | Recommendation | +| -------------------- | ---------------------------------------- | +| Simple test setup | Shakapacker `compile: true` | +| Large test suite | React on Rails test helper | +| Parallel testing | React on Rails test helper or precompile | +| CI/CD pipeline | Precompile before tests | +| Quick local tests | Shakapacker `compile: true` | +| Custom build command | React on Rails test helper | + +## Related Documentation + +- [Configuration Reference](../api-reference/configuration.md#build_test_command) +- [Shakapacker Configuration](https://github.com/shakacode/shakapacker#configuration) +- [RSpec Configuration](../building-features/rspec-configuration.md) +- [Minitest Configuration](../building-features/minitest-configuration.md) + +## Need Help? + +- **Forum:** [ShakaCode Forum](https://forum.shakacode.com/) +- **Docs:** [React on Rails Guides](https://www.shakacode.com/react-on-rails/docs/) +- **Support:** [justin@shakacode.com](mailto:justin@shakacode.com) diff --git a/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt b/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt index f1b1a8665d..8015f38971 100644 --- a/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +++ b/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt @@ -1,67 +1,45 @@ # frozen_string_literal: true -# See https://github.com/shakacode/react_on_rails/blob/master/docs/guides/configuration.md -# for many more options. +# React on Rails configuration +# See https://github.com/shakacode/react_on_rails/blob/master/docs/api-reference/configuration.md +# for complete documentation of all configuration options. ReactOnRails.configure do |config| - # This configures the script to run to build the production assets by webpack. Set this to nil - # if you don't want react_on_rails building this file for you. - # If nil, then the standard shakacode/shakapacker assets:precompile will run - # config.build_production_command = nil - - ################################################################################ ################################################################################ - # TEST CONFIGURATION OPTIONS - # Below options are used with the use of this test helper: - # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + # Server Rendering (Recommended) ################################################################################ - - # If you are using this in your spec_helper.rb (or rails_helper.rb): - # - # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - # - # with rspec then this controls what npm command is run - # to automatically refresh your webpack assets on every test run. - # - # Alternately, you can remove the `ReactOnRails::TestHelper.configure_rspec_to_compile_assets` - # and set the config/shakapacker.yml option for test to true. - config.build_test_command = "RAILS_ENV=test bin/shakapacker" + # Configure server bundle for server-side rendering with `prerender: true` + # Set to "" if you're not using server rendering + config.server_bundle_js_file = "server-bundle.js" ################################################################################ + # Test Configuration (Optional) ################################################################################ - # SERVER RENDERING OPTIONS - ################################################################################ - # This is the file used for server rendering of React when using `(prerender: true)` - # If you are never using server rendering, you should set this to "". - # Note, there is only one server bundle, unlike JavaScript where you want to minimize the size - # of the JS sent to the client. For the server rendering, React on Rails creates a pool of - # JavaScript execution instances which should handle any component requested. + # โš ๏ธ IMPORTANT: Two mutually exclusive approaches - use ONLY ONE: # - # While you may configure this to be the same as your client bundle file, this file is typically - # different. You should have ONE server bundle which can create all of your server rendered - # React components. + # RECOMMENDED APPROACH: Set `compile: true` in config/shakapacker.yml test section + # - Simpler configuration (no additional setup needed) + # - Handled automatically by Shakapacker # - config.server_bundle_js_file = "server-bundle.js" - - # Configure where server bundles are output. Defaults to "ssr-generated". - # This should match your webpack configuration for server bundles. - config.server_bundle_output_path = "ssr-generated" - - # Enforce that server bundles are only loaded from private (non-public) directories. - # When true, server bundles will only be loaded from the configured server_bundle_output_path. - # This is recommended for production to prevent server-side code from being exposed. - config.enforce_private_server_bundles = true + # ALTERNATIVE APPROACH: Uncomment below AND configure ReactOnRails::TestHelper + # - Provides explicit control over test asset compilation timing + # - Requires adding ReactOnRails::TestHelper to spec/rails_helper.rb + # - See: https://github.com/shakacode/react_on_rails/blob/master/docs/guides/testing-configuration.md + # + # config.build_test_command = "RAILS_ENV=test bin/shakapacker" ################################################################################ + # Advanced Configuration ################################################################################ - # FILE SYSTEM BASED COMPONENT REGISTRY - ################################################################################ - # `components_subdirectory` is the name of the matching directories that contain automatically registered components - # for use in the Rails views. The default is nil, you can enable the feature by updating it in the next line. - config.components_subdirectory = "ror_components" + # Most configuration options have sensible defaults and don't need to be set. + # For advanced options including: + # - File-based component registry (components_subdirectory, auto_load_bundle) + # - Component loading strategies (async/defer/sync) + # - Server bundle security and organization + # - I18n configuration + # - Server rendering pool configuration + # - Custom rendering extensions + # - And more... # - # For automated component registry, `render_component` view helper method tries to load bundle for component from - # generated directory. default is false, you can pass option at the time of individual usage or update the default - # in the following line - config.auto_load_bundle = true + # See: https://github.com/shakacode/react_on_rails/blob/master/docs/api-reference/configuration.md end diff --git a/lib/react_on_rails/doctor.rb b/lib/react_on_rails/doctor.rb index efe5f80006..1a7eeed09a 100644 --- a/lib/react_on_rails/doctor.rb +++ b/lib/react_on_rails/doctor.rb @@ -154,6 +154,7 @@ def check_key_files def check_configuration_details check_shakapacker_configuration_details check_react_on_rails_configuration_details + check_server_bundle_prerender_consistency end def check_bin_dev_launcher @@ -166,6 +167,7 @@ def check_bin_dev_launcher def check_testing_setup check_rspec_helper_setup + check_build_test_configuration end def check_development @@ -1110,6 +1112,124 @@ def exit_with_status end end + # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + def check_server_bundle_prerender_consistency + config_path = "config/initializers/react_on_rails.rb" + return unless File.exist?(config_path) + + checker.add_info("\n๐Ÿ” Server Rendering Consistency:") + + begin + content = File.read(config_path) + + # Check for server bundle configuration + server_bundle_match = content.match(/config\.server_bundle_js_file\s*=\s*["']([^"']+)["']/) + server_bundle_set = server_bundle_match && server_bundle_match[1].present? + + # Check for global prerender setting + prerender_match = content.match(/config\.prerender\s*=\s*(true)/) + prerender_set = prerender_match + + # Check if prerender is used in views + uses_prerender = uses_prerender_in_views? + + # Analyze the configuration + if (prerender_set || uses_prerender) && !server_bundle_set + checker.add_warning(" โš ๏ธ Server rendering is enabled but server_bundle_js_file is not configured") + checker.add_info(" ๐Ÿ’ก Set config.server_bundle_js_file = 'server-bundle.js' to enable SSR") + checker.add_info(" ๐Ÿ’ก See: https://www.shakacode.com/react-on-rails/docs/guides/server-rendering") + elsif server_bundle_set && !prerender_set && !uses_prerender + checker.add_info(" โ„น๏ธ server_bundle_js_file is configured but prerender doesn't appear to be used") + checker.add_info(" ๐Ÿ’ก Either use prerender: true in react_component calls or remove server_bundle_js_file") + else + checker.add_success(" โœ… Server rendering configuration is consistent") + end + rescue StandardError => e + checker.add_warning(" โš ๏ธ Could not analyze server rendering configuration: #{e.message}") + end + end + # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + + def uses_prerender_in_views? + # Check view files for prerender: true + view_files = Dir.glob("app/views/**/*.{erb,haml,slim}") + view_files.any? do |file| + next unless File.exist?(file) + + File.read(file).match?(/prerender:\s*true/) + end + rescue StandardError + false + end + + # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength + def check_build_test_configuration + config_path = "config/initializers/react_on_rails.rb" + shakapacker_yml = "config/shakapacker.yml" + + return unless File.exist?(config_path) + + checker.add_info("\n๐Ÿงช Test Asset Compilation:") + + begin + config_content = File.read(config_path) + has_build_test_command = config_content.match(/^\s*config\.build_test_command\s*=\s*["']([^"']+)["']/) + uses_test_helper = uses_react_on_rails_test_helper? + + if File.exist?(shakapacker_yml) + shakapacker_content = File.read(shakapacker_yml) + # Match test section and look for compile: true + has_compile_true = shakapacker_content.match(/^test:.*?^\s+compile:\s*true/m) + + if has_build_test_command && has_compile_true + checker.add_warning(" โš ๏ธ Both build_test_command and shakapacker compile: true are configured") + checker.add_info(" ๐Ÿ’ก These are mutually exclusive - use only one approach") + checker.add_info(" ๐Ÿ’ก Recommended: Use compile: true in shakapacker.yml (simpler)") + checker.add_info(" ๐Ÿ’ก Alternative: Use build_test_command with ReactOnRails::TestHelper (explicit control)") + checker.add_info(" ๐Ÿ“– See: https://github.com/shakacode/react_on_rails/blob/master/docs/guides/testing-configuration.md") + elsif has_build_test_command && !uses_test_helper + checker.add_warning(" โš ๏ธ build_test_command is set but ReactOnRails::TestHelper is not configured") + checker.add_info(" ๐Ÿ’ก Add to spec/rails_helper.rb:") + checker.add_info(" ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)") + checker.add_info(" ๐Ÿ’ก Or remove build_test_command and use compile: true in shakapacker.yml") + elsif !has_build_test_command && uses_test_helper + checker.add_error(" ๐Ÿšซ ReactOnRails::TestHelper is configured but build_test_command is not set") + checker.add_info(" ๐Ÿ’ก Add to config/initializers/react_on_rails.rb:") + checker.add_info(" config.build_test_command = 'RAILS_ENV=test bin/shakapacker'") + checker.add_info(" ๐Ÿ’ก Or remove TestHelper and use compile: true in shakapacker.yml") + elsif !has_build_test_command && !has_compile_true && !uses_test_helper + checker.add_warning(" โš ๏ธ No test asset compilation configured") + checker.add_info(" ๐Ÿ’ก Recommended: Add to shakapacker.yml test section:") + checker.add_info(" compile: true") + checker.add_info(" ๐Ÿ“– See: https://github.com/shakacode/react_on_rails/blob/master/docs/guides/testing-configuration.md") + elsif has_compile_true + checker.add_success(" โœ… Test assets configured via Shakapacker auto-compilation") + checker.add_info(" (compile: true in shakapacker.yml)") + elsif has_build_test_command && uses_test_helper + checker.add_success(" โœ… Test assets configured via React on Rails test helper") + checker.add_info(" (build_test_command + ReactOnRails::TestHelper)") + end + else + checker.add_warning(" โš ๏ธ config/shakapacker.yml not found") + end + rescue StandardError => e + checker.add_warning(" โš ๏ธ Could not analyze test configuration: #{e.message}") + end + end + # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength + + def uses_react_on_rails_test_helper? + spec_helpers = ["spec/rails_helper.rb", "spec/spec_helper.rb", "test/test_helper.rb"] + spec_helpers.any? do |helper| + next unless File.exist?(helper) + + content = File.read(helper) + content.include?("configure_rspec_to_compile_assets") || content.include?("ensure_assets_compiled") + end + rescue StandardError + false + end + def relativize_path(absolute_path) return absolute_path unless absolute_path.is_a?(String) diff --git a/react_on_rails_pro/spec/dummy/config/initializers/react_on_rails.rb b/react_on_rails_pro/spec/dummy/config/initializers/react_on_rails.rb index fb674fecd6..ed8a0038ba 100644 --- a/react_on_rails_pro/spec/dummy/config/initializers/react_on_rails.rb +++ b/react_on_rails_pro/spec/dummy/config/initializers/react_on_rails.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true -# For documentation of parameters see: docs/basics/configuration.md +# โš ๏ธ TEST CONFIGURATION - Do not copy directly for production apps +# This is the Pro dummy app configuration used for testing React on Rails Pro features. +# See docs/api-reference/configuration.md for production configuration guidance. + +# Advanced: Custom rendering extension to add values to railsContext module RenderingExtension - # Return a Hash that contains custom values from the view context that will get passed to - # all calls to react_component and redux_store for rendering def self.custom_context(view_context) if view_context.controller.is_a?(ActionMailer::Base) {} @@ -15,6 +17,7 @@ def self.custom_context(view_context) end end +# Advanced: Custom props extension for client-side hydration module RenderingPropsExtension def self.adjust_props_for_client_side_hydration(_component_name, props) if props.instance_of?(Hash) @@ -26,19 +29,32 @@ def self.adjust_props_for_client_side_hydration(_component_name, props) end ReactOnRails.configure do |config| + ################################################################################ + # Essential Configuration + ################################################################################ config.server_bundle_js_file = "server-bundle.js" + config.components_subdirectory = "ror-auto-load-components" + config.auto_load_bundle = true + + ################################################################################ + # Pro Feature Testing: Server Bundle Security + ################################################################################ + # Testing private server bundle enforcement (recommended for production) + config.enforce_private_server_bundles = true + config.server_bundle_output_path = "ssr-generated" + + ################################################################################ + # Test-specific Advanced Configuration + ################################################################################ + # Testing with fixed DOM IDs for easier test assertions config.random_dom_id = false # default is true - # Next 2 lines are commented out because we've set test.compile to true - # config.build_test_command = "yarn run build:test" - # config.webpack_generated_files = %w[server-bundle.js manifest.json] + # Testing advanced rendering customization config.rendering_extension = RenderingExtension - config.rendering_props_extension = RenderingPropsExtension - config.auto_load_bundle = true - config.components_subdirectory = "ror-auto-load-components" - - config.enforce_private_server_bundles = true - config.server_bundle_output_path = "ssr-generated" + # NOTE: build_test_command and webpack_generated_files are commented out + # because we've set test.compile to true in shakapacker.yml + # config.build_test_command = "yarn run build:test" + # config.webpack_generated_files = %w[server-bundle.js manifest.json] end diff --git a/react_on_rails_pro/spec/execjs-compatible-dummy/config/initializers/react_on_rails.rb b/react_on_rails_pro/spec/execjs-compatible-dummy/config/initializers/react_on_rails.rb index 12cc694578..13b7a15569 100644 --- a/react_on_rails_pro/spec/execjs-compatible-dummy/config/initializers/react_on_rails.rb +++ b/react_on_rails_pro/spec/execjs-compatible-dummy/config/initializers/react_on_rails.rb @@ -1,58 +1,31 @@ # frozen_string_literal: true -# See https://github.com/shakacode/react_on_rails/blob/master/docs/guides/configuration.md -# for many more options. +# โš ๏ธ TEST CONFIGURATION - Do not copy directly for production apps +# This is the ExecJS-compatible dummy app for testing legacy webpacker compatibility. +# See docs/api-reference/configuration.md for production configuration guidance. ReactOnRails.configure do |config| - # This configures the script to run to build the production assets by webpack. Set this to nil - # if you don't want react_on_rails building this file for you. - # If nil, then the standard shakacode/webpacker assets:precompile will run - # config.build_production_command = nil - - ################################################################################ ################################################################################ - # TEST CONFIGURATION OPTIONS - # Below options are used with the use of this test helper: - # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) + # Essential Configuration ################################################################################ + # Configure server bundle for server-side rendering + config.server_bundle_js_file = "server-bundle.js" - # If you are using this in your spec_helper.rb (or rails_helper.rb): - # - # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - # - # with rspec then this controls what yarn command is run - # to automatically refresh your webpack assets on every test run. - # - # Alternately, you can remove the `ReactOnRails::TestHelper.configure_rspec_to_compile_assets` - # and set the config/webpacker.yml option for test to true. + # Test configuration config.build_test_command = "RAILS_ENV=test bin/webpacker" ################################################################################ + # File System Based Component Registry (Optional - Disabled for this test) ################################################################################ - # SERVER RENDERING OPTIONS - ################################################################################ - # This is the file used for server rendering of React when using `(prerender: true)` - # If you are never using server rendering, you should set this to "". - # Note, there is only one server bundle, unlike JavaScript where you want to minimize the size - # of the JS sent to the client. For the server rendering, React on Rails creates a pool of - # JavaScript execution instances which should handle any component requested. - # - # While you may configure this to be the same as your client bundle file, this file is typically - # different. You should have ONE server bundle which can create all of your server rendered - # React components. - # - config.server_bundle_js_file = "server-bundle.js" + # Uncomment to enable automatic component registration: + # config.components_subdirectory = "ror_components" + # config.auto_load_bundle = true + config.auto_load_bundle = false ################################################################################ + # Advanced Configuration ################################################################################ - # FILE SYSTEM BASED COMPONENT REGISTRY - ################################################################################ - # `components_subdirectory` is the name of the matching directories that contain automatically registered components - # for use in the Rails views. The default is nil, you can enable the feature by updating it in the next line. - # config.components_subdirectory = "ror_components" - # - # For automated component registry, `render_component` view helper method tries to load bundle for component from - # generated directory. default is false, you can pass option at the time of individual usage or update the default - # in the following line - config.auto_load_bundle = false + # Most options have sensible defaults. For advanced configuration including + # component loading strategies, server bundle security, and more, see: + # https://github.com/shakacode/react_on_rails/blob/master/docs/api-reference/configuration.md end diff --git a/spec/dummy/config/initializers/react_on_rails.rb b/spec/dummy/config/initializers/react_on_rails.rb index 83598f028a..440699c09e 100644 --- a/spec/dummy/config/initializers/react_on_rails.rb +++ b/spec/dummy/config/initializers/react_on_rails.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true -# For documentation of parameters see: docs/basics/configuration.md +# โš ๏ธ TEST CONFIGURATION - Do not copy directly for production apps +# This is the dummy app configuration used for testing React on Rails features. +# See docs/api-reference/configuration.md for production configuration guidance. + +# Advanced: Custom rendering extension to add values to railsContext module RenderingExtension - # Return a Hash that contains custom values from the view context that will get passed to - # all calls to react_component and redux_store for rendering def self.custom_context(view_context) if view_context.controller.is_a?(ActionMailer::Base) {} @@ -15,8 +17,8 @@ def self.custom_context(view_context) end end +# Advanced: Custom props extension for client-side hydration module RenderingPropsExtension - # Return a Hash that contains custom props for all client rendered react_components def self.adjust_props_for_client_side_hydration(component_name, props) props[:modificationTarget] = "client-only" if component_name == "HelloWorldProps" props @@ -24,22 +26,27 @@ def self.adjust_props_for_client_side_hydration(component_name, props) end ReactOnRails.configure do |config| + ################################################################################ + # Essential Configuration + ################################################################################ config.server_bundle_js_file = "server-bundle.js" - config.random_dom_id = false # default is true + config.components_subdirectory = "startup" + config.auto_load_bundle = true - # Set server_bundle_output_path to nil so bundles are read from public path - # This ensures the dummy app uses the standard webpack output location + ################################################################################ + # Test-specific Advanced Configuration + ################################################################################ + # Testing server bundles from public path instead of private directory config.server_bundle_output_path = nil config.enforce_private_server_bundles = false - # Uncomment to test these - # config.build_test_command = "yarn run build:test" - # config.build_production_command = "RAILS_ENV=production NODE_ENV=production bin/shakapacker" - # config.webpack_generated_files = %w[server-bundle.js manifest.json] - config.rendering_extension = RenderingExtension + # Testing with fixed DOM IDs for easier test assertions + config.random_dom_id = false # default is true - config.rendering_props_extension = RenderingPropsExtension - config.components_subdirectory = "startup" - config.auto_load_bundle = true + # Testing with explicit loading strategy config.generated_component_packs_loading_strategy = :defer + + # Testing advanced rendering customization + config.rendering_extension = RenderingExtension + config.rendering_props_extension = RenderingPropsExtension end