Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Changes since the last non-beta release.

#### Added

- **Rspack Support**: Added `--rspack` flag to `react_on_rails:install` generator for significantly faster builds (~20x improvement with SWC). Includes unified webpack/rspack configuration templates and `bin/switch-bundler` utility to switch between bundlers post-installation. [PR #1852](https://github.com/shakacode/react_on_rails/pull/1852) by [justin808](https://github.com/justin808).

- **Attribution Comment**: Added HTML comment attribution to Rails views containing React on Rails functionality. The comment automatically displays which version is in use (open source React on Rails or React on Rails Pro) and, for Pro users, shows the license status. This helps identify React on Rails usage across your application. [PR #1857](https://github.com/shakacode/react_on_rails/pull/1857) by [AbanoubGhadban](https://github.com/AbanoubGhadban).

- **Improved Error Messages**: Error messages for version mismatches and package configuration issues now include package-manager-specific installation commands (npm, yarn, pnpm, bun). [PR #1881](https://github.com/shakacode/react_on_rails/pull/1881) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
Expand Down
160 changes: 160 additions & 0 deletions RSPACK_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Rspack Generator Option Implementation

This document summarizes the implementation of the `--rspack` option for the React on Rails generator, based on the patterns from [PR #20 in react_on_rails-demos](https://github.com/shakacode/react_on_rails-demos/pull/20).

## Overview

The `--rspack` flag allows users to generate a React on Rails application using Rspack instead of Webpack as the bundler. Rspack provides significantly faster build times (~53-270ms vs typical webpack builds).

## Changes Made

### 1. Install Generator (`lib/generators/react_on_rails/install_generator.rb`)

- **Added `--rspack` class option** (line 31-35): Boolean flag to enable Rspack bundler
- **Updated `invoke_generators`** (line 82-83): Pass rspack option to base generator
- **Added `add_rspack_dependencies` method** (line 499-513): Installs Rspack core packages:
- `@rspack/core`
- `rspack-manifest-plugin`
- **Updated `add_dev_dependencies`** (line 515-534): Conditionally installs rspack or webpack refresh plugins:
- Rspack: `@rspack/cli`, `@rspack/plugin-react-refresh`, `react-refresh`
- Webpack: `@pmmmwh/react-refresh-webpack-plugin`, `react-refresh`
- **Updated `add_js_dependencies`** (line 433): Calls `add_rspack_dependencies` when rspack flag is set

### 2. Base Generator (`lib/generators/react_on_rails/base_generator.rb`)

- **Added `--rspack` class option** (line 22-26): Boolean flag (passed from install generator)
- **Updated `copy_packer_config`** (line 85-100): Calls `configure_rspack_in_shakapacker` after copying config
- **Added `configure_rspack_in_shakapacker` method** (line 404-426):
- Adds `assets_bundler: 'rspack'` to shakapacker.yml default section
- Changes `webpack_loader` to `'swc'` (Rspack works best with SWC transpiler)

### 3. Webpack Configuration Templates

Updated webpack configuration templates to support both webpack and rspack bundlers with unified config approach:

**development.js.tt**:

- Added `config` to shakapacker require to access `assets_bundler` setting
- Conditional React Refresh plugin loading based on `config.assets_bundler`:
- Rspack: Uses `@rspack/plugin-react-refresh`
- Webpack: Uses `@pmmmwh/react-refresh-webpack-plugin`
- Prevents "window not found" errors when using rspack

**serverWebpackConfig.js.tt**:

- Added `bundler` variable that conditionally requires `@rspack/core` or `webpack`
- Changed `webpack.optimize.LimitChunkCountPlugin` to `bundler.optimize.LimitChunkCountPlugin`
- Enables same config to work with both bundlers without warnings
- Avoids hardcoding webpack-specific imports

### 4. Bundler Switching Script (`lib/generators/react_on_rails/templates/base/base/bin/switch-bundler`)

Created a new executable script that allows switching between webpack and rspack after installation:

**Features:**

- Updates `shakapacker.yml` with correct `assets_bundler` setting
- Switches `webpack_loader` between 'swc' (rspack) and 'babel' (webpack)
- Removes old bundler dependencies from package.json
- Installs new bundler dependencies
- Supports npm, yarn, and pnpm package managers
- Auto-detects package manager from lock files

**Usage:**

```bash
bin/switch-bundler rspack # Switch to Rspack
bin/switch-bundler webpack # Switch to Webpack
```

**Dependencies managed:**

- **Webpack**: webpack, webpack-cli, webpack-dev-server, webpack-assets-manifest, webpack-merge, @pmmmwh/react-refresh-webpack-plugin
- **Rspack**: @rspack/core, @rspack/cli, @rspack/plugin-react-refresh, rspack-manifest-plugin

## Usage

### Generate new app with Rspack:

```bash
rails generate react_on_rails:install --rspack
```

### Generate with Rspack and TypeScript:

```bash
rails generate react_on_rails:install --rspack --typescript
```

### Generate with Rspack and Redux:

```bash
rails generate react_on_rails:install --rspack --redux
```

### Switch existing app to Rspack:

```bash
bin/switch-bundler rspack
```

## Configuration Changes

When `--rspack` is used, the following configuration changes are applied to `config/shakapacker.yml`:

```yaml
default: &default
source_path: app/javascript
assets_bundler: 'rspack' # Added
# ... other settings
webpack_loader: 'swc' # Changed from 'babel'
```

## Dependencies

### Rspack-specific packages installed:

**Production:**

- `@rspack/core` - Core Rspack bundler
- `rspack-manifest-plugin` - Manifest generation for Rspack

**Development:**

- `@rspack/cli` - Rspack CLI tools
- `@rspack/plugin-react-refresh` - React Fast Refresh for Rspack
- `react-refresh` - React Fast Refresh runtime

### Webpack packages NOT installed with --rspack:

**Production:**

- `webpack`
- `webpack-assets-manifest`
- `webpack-merge`

**Development:**

- `webpack-cli`
- `webpack-dev-server`
- `@pmmmwh/react-refresh-webpack-plugin`

## Performance Benefits

According to PR #20:

- Build times: ~53-270ms with Rspack vs typical webpack builds
- Approximately 20x faster transpilation with SWC (used by Rspack)
- Faster development builds and CI runs

## Testing

The implementation follows existing generator patterns and passes RuboCop checks with zero offenses.

## Compatibility

- Works with existing webpack configuration files (unified config approach)
- Compatible with TypeScript option (`--typescript`)
- Compatible with Redux option (`--redux`)
- Supports all package managers (npm, yarn, pnpm)
- Reversible via `bin/switch-bundler` script
27 changes: 27 additions & 0 deletions lib/generators/react_on_rails/base_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ class BaseGenerator < Rails::Generators::Base
desc: "Install Redux package and Redux version of Hello World Example",
aliases: "-R"

# --rspack
class_option :rspack,
type: :boolean,
default: false,
desc: "Use Rspack instead of Webpack as the bundler"

def add_hello_world_route
route "get 'hello_world', to: 'hello_world#index'"
end
Expand Down Expand Up @@ -82,13 +88,15 @@ def copy_packer_config
if File.exist?(".shakapacker_just_installed")
puts "Skipping Shakapacker config copy (already installed by Shakapacker installer)"
File.delete(".shakapacker_just_installed") # Clean up marker
configure_rspack_in_shakapacker if options.rspack?
return
end

puts "Adding Shakapacker #{ReactOnRails::PackerUtils.shakapacker_version} config"
base_path = "base/base/"
config = "config/shakapacker.yml"
copy_file("#{base_path}#{config}", config)
configure_rspack_in_shakapacker if options.rspack?
end

def add_base_gems_to_gemfile
Expand Down Expand Up @@ -392,6 +400,25 @@ def add_configure_rspec_to_compile_assets(helper_file)
search_str = "RSpec.configure do |config|"
gsub_file(helper_file, search_str, CONFIGURE_RSPEC_TO_COMPILE_ASSETS)
end

def configure_rspack_in_shakapacker
shakapacker_config_path = "config/shakapacker.yml"
return unless File.exist?(shakapacker_config_path)

puts Rainbow("🔧 Configuring Shakapacker for Rspack...").yellow

# Parse YAML config properly to avoid fragile regex manipulation
config = YAML.load_file(shakapacker_config_path)

# Update default section
config["default"] ||= {}
config["default"]["assets_bundler"] = "rspack"
config["default"]["webpack_loader"] = "swc"

# Write back as YAML
File.write(shakapacker_config_path, YAML.dump(config))
puts Rainbow("✅ Updated shakapacker.yml for Rspack").green
end
end
end
end
42 changes: 37 additions & 5 deletions lib/generators/react_on_rails/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ class InstallGenerator < Rails::Generators::Base
desc: "Generate TypeScript files and install TypeScript dependencies. Default: false",
aliases: "-T"

# --rspack
class_option :rspack,
type: :boolean,
default: false,
desc: "Use Rspack instead of Webpack as the bundler. Default: false"

# --ignore-warnings
class_option :ignore_warnings,
type: :boolean,
Expand Down Expand Up @@ -96,7 +102,8 @@ def invoke_generators
create_css_module_types
create_typescript_config
end
invoke "react_on_rails:base", [], { typescript: options.typescript?, redux: options.redux? }
invoke "react_on_rails:base", [],
{ typescript: options.typescript?, redux: options.redux?, rspack: options.rspack? }
if options.redux?
invoke "react_on_rails:react_with_redux", [], { typescript: options.typescript? }
else
Expand Down Expand Up @@ -447,6 +454,7 @@ def add_js_dependencies
add_react_on_rails_package
add_react_dependencies
add_css_dependencies
add_rspack_dependencies if options.rspack?
add_dev_dependencies
end

Expand Down Expand Up @@ -512,12 +520,36 @@ def add_css_dependencies
handle_npm_failure("CSS dependencies", css_deps) unless success
end

def add_rspack_dependencies
puts "Installing Rspack core dependencies..."
rspack_deps = %w[
@rspack/core
rspack-manifest-plugin
]
if add_npm_dependencies(rspack_deps)
@added_dependencies_to_package_json = true
return
end

success = system("npm", "install", *rspack_deps)
@ran_direct_installs = true if success
handle_npm_failure("Rspack dependencies", rspack_deps) unless success
end

def add_dev_dependencies
puts "Installing development dependencies..."
dev_deps = %w[
@pmmmwh/react-refresh-webpack-plugin
react-refresh
]
dev_deps = if options.rspack?
%w[
@rspack/cli
@rspack/plugin-react-refresh
react-refresh
]
else
%w[
@pmmmwh/react-refresh-webpack-plugin
react-refresh
]
end
if add_npm_dependencies(dev_deps, dev: true)
@added_dependencies_to_package_json = true
return
Expand Down
Loading
Loading