Skip to content

Commit 8275cac

Browse files
justin808claude
andcommitted
Add --rspack generator option for faster builds
Implements --rspack flag for react_on_rails:install generator to use Rspack bundler instead of Webpack, providing ~20x faster builds. ## Changes - Added --rspack option to install_generator.rb and base_generator.rb - Conditional dependency installation (rspack vs webpack packages) - Auto-configures shakapacker.yml for rspack with SWC transpiler - Created bin/switch-bundler utility to switch bundlers post-install ## Key Features - Rspack packages: @rspack/core, @rspack/cli, @rspack/plugin-react-refresh - Compatible with --typescript and --redux options - Reversible via bin/switch-bundler script - Supports npm, yarn, and pnpm package managers ## Usage rails generate react_on_rails:install --rspack bin/switch-bundler rspack # Switch existing app Based on patterns from react_on_rails-demos PR #20 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 1a84b4a commit 8275cac

File tree

4 files changed

+353
-5
lines changed

4 files changed

+353
-5
lines changed

RSPACK_IMPLEMENTATION.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Rspack Generator Option Implementation
2+
3+
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).
4+
5+
## Overview
6+
7+
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).
8+
9+
## Changes Made
10+
11+
### 1. Install Generator (`lib/generators/react_on_rails/install_generator.rb`)
12+
13+
- **Added `--rspack` class option** (line 31-35): Boolean flag to enable Rspack bundler
14+
- **Updated `invoke_generators`** (line 82-83): Pass rspack option to base generator
15+
- **Added `add_rspack_dependencies` method** (line 499-513): Installs Rspack core packages:
16+
- `@rspack/core`
17+
- `rspack-manifest-plugin`
18+
- **Updated `add_dev_dependencies`** (line 515-534): Conditionally installs rspack or webpack refresh plugins:
19+
- Rspack: `@rspack/cli`, `@rspack/plugin-react-refresh`, `react-refresh`
20+
- Webpack: `@pmmmwh/react-refresh-webpack-plugin`, `react-refresh`
21+
- **Updated `add_js_dependencies`** (line 433): Calls `add_rspack_dependencies` when rspack flag is set
22+
23+
### 2. Base Generator (`lib/generators/react_on_rails/base_generator.rb`)
24+
25+
- **Added `--rspack` class option** (line 22-26): Boolean flag (passed from install generator)
26+
- **Updated `copy_packer_config`** (line 85-100): Calls `configure_rspack_in_shakapacker` after copying config
27+
- **Added `configure_rspack_in_shakapacker` method** (line 404-426):
28+
- Adds `assets_bundler: 'rspack'` to shakapacker.yml default section
29+
- Changes `webpack_loader` to `'swc'` (Rspack works best with SWC transpiler)
30+
31+
### 3. Bundler Switching Script (`lib/generators/react_on_rails/templates/base/base/bin/switch-bundler`)
32+
33+
Created a new executable script that allows switching between webpack and rspack after installation:
34+
35+
**Features:**
36+
37+
- Updates `shakapacker.yml` with correct `assets_bundler` setting
38+
- Switches `webpack_loader` between 'swc' (rspack) and 'babel' (webpack)
39+
- Removes old bundler dependencies from package.json
40+
- Installs new bundler dependencies
41+
- Supports npm, yarn, and pnpm package managers
42+
- Auto-detects package manager from lock files
43+
44+
**Usage:**
45+
46+
```bash
47+
bin/switch-bundler rspack # Switch to Rspack
48+
bin/switch-bundler webpack # Switch to Webpack
49+
```
50+
51+
**Dependencies managed:**
52+
53+
- **Webpack**: webpack, webpack-cli, webpack-dev-server, webpack-assets-manifest, webpack-merge, @pmmmwh/react-refresh-webpack-plugin
54+
- **Rspack**: @rspack/core, @rspack/cli, @rspack/plugin-react-refresh, rspack-manifest-plugin
55+
56+
## Usage
57+
58+
### Generate new app with Rspack:
59+
60+
```bash
61+
rails generate react_on_rails:install --rspack
62+
```
63+
64+
### Generate with Rspack and TypeScript:
65+
66+
```bash
67+
rails generate react_on_rails:install --rspack --typescript
68+
```
69+
70+
### Generate with Rspack and Redux:
71+
72+
```bash
73+
rails generate react_on_rails:install --rspack --redux
74+
```
75+
76+
### Switch existing app to Rspack:
77+
78+
```bash
79+
bin/switch-bundler rspack
80+
```
81+
82+
## Configuration Changes
83+
84+
When `--rspack` is used, the following configuration changes are applied to `config/shakapacker.yml`:
85+
86+
```yaml
87+
default: &default
88+
source_path: app/javascript
89+
assets_bundler: 'rspack' # Added
90+
# ... other settings
91+
webpack_loader: 'swc' # Changed from 'babel'
92+
```
93+
94+
## Dependencies
95+
96+
### Rspack-specific packages installed:
97+
98+
**Production:**
99+
100+
- `@rspack/core` - Core Rspack bundler
101+
- `rspack-manifest-plugin` - Manifest generation for Rspack
102+
103+
**Development:**
104+
105+
- `@rspack/cli` - Rspack CLI tools
106+
- `@rspack/plugin-react-refresh` - React Fast Refresh for Rspack
107+
- `react-refresh` - React Fast Refresh runtime
108+
109+
### Webpack packages NOT installed with --rspack:
110+
111+
**Production:**
112+
113+
- `webpack`
114+
- `webpack-assets-manifest`
115+
- `webpack-merge`
116+
117+
**Development:**
118+
119+
- `webpack-cli`
120+
- `webpack-dev-server`
121+
- `@pmmmwh/react-refresh-webpack-plugin`
122+
123+
## Performance Benefits
124+
125+
According to PR #20:
126+
127+
- Build times: ~53-270ms with Rspack vs typical webpack builds
128+
- Approximately 20x faster transpilation with SWC (used by Rspack)
129+
- Faster development builds and CI runs
130+
131+
## Testing
132+
133+
The implementation follows existing generator patterns and passes RuboCop checks with zero offenses.
134+
135+
## Compatibility
136+
137+
- Works with existing webpack configuration files (unified config approach)
138+
- Compatible with TypeScript option (`--typescript`)
139+
- Compatible with Redux option (`--redux`)
140+
- Supports all package managers (npm, yarn, pnpm)
141+
- Reversible via `bin/switch-bundler` script

lib/generators/react_on_rails/base_generator.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ class BaseGenerator < Rails::Generators::Base
1919
desc: "Install Redux package and Redux version of Hello World Example",
2020
aliases: "-R"
2121

22+
# --rspack
23+
class_option :rspack,
24+
type: :boolean,
25+
default: false,
26+
desc: "Use Rspack instead of Webpack as the bundler"
27+
2228
def add_hello_world_route
2329
route "get 'hello_world', to: 'hello_world#index'"
2430
end
@@ -82,13 +88,15 @@ def copy_packer_config
8288
if File.exist?(".shakapacker_just_installed")
8389
puts "Skipping Shakapacker config copy (already installed by Shakapacker installer)"
8490
File.delete(".shakapacker_just_installed") # Clean up marker
91+
configure_rspack_in_shakapacker if options.rspack?
8592
return
8693
end
8794

8895
puts "Adding Shakapacker #{ReactOnRails::PackerUtils.shakapacker_version} config"
8996
base_path = "base/base/"
9097
config = "config/shakapacker.yml"
9198
copy_file("#{base_path}#{config}", config)
99+
configure_rspack_in_shakapacker if options.rspack?
92100
end
93101

94102
def add_base_gems_to_gemfile
@@ -392,6 +400,30 @@ def add_configure_rspec_to_compile_assets(helper_file)
392400
search_str = "RSpec.configure do |config|"
393401
gsub_file(helper_file, search_str, CONFIGURE_RSPEC_TO_COMPILE_ASSETS)
394402
end
403+
404+
def configure_rspack_in_shakapacker
405+
shakapacker_config_path = "config/shakapacker.yml"
406+
return unless File.exist?(shakapacker_config_path)
407+
408+
puts Rainbow("🔧 Configuring Shakapacker for Rspack...").yellow
409+
410+
# Read the current config
411+
config_content = File.read(shakapacker_config_path)
412+
413+
# Update assets_bundler to rspack in default section
414+
unless config_content.include?("assets_bundler:")
415+
# Add assets_bundler after source_path in default section
416+
config_content.gsub!(/^default: &default\n(\s+source_path:.*\n)/) do
417+
"default: &default\n#{Regexp.last_match(1)} assets_bundler: 'rspack'\n"
418+
end
419+
end
420+
421+
# Update webpack_loader to swc (rspack works best with SWC)
422+
config_content.gsub!(/^\s*webpack_loader:.*$/, " webpack_loader: 'swc'")
423+
424+
File.write(shakapacker_config_path, config_content)
425+
puts Rainbow("✅ Updated shakapacker.yml for Rspack").green
426+
end
395427
end
396428
end
397429
end

lib/generators/react_on_rails/install_generator.rb

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ class InstallGenerator < Rails::Generators::Base
2828
desc: "Generate TypeScript files and install TypeScript dependencies. Default: false",
2929
aliases: "-T"
3030

31+
# --rspack
32+
class_option :rspack,
33+
type: :boolean,
34+
default: false,
35+
desc: "Use Rspack instead of Webpack as the bundler. Default: false"
36+
3137
# --ignore-warnings
3238
class_option :ignore_warnings,
3339
type: :boolean,
@@ -73,7 +79,8 @@ def invoke_generators
7379
create_css_module_types
7480
create_typescript_config
7581
end
76-
invoke "react_on_rails:base", [], { typescript: options.typescript?, redux: options.redux? }
82+
invoke "react_on_rails:base", [],
83+
{ typescript: options.typescript?, redux: options.redux?, rspack: options.rspack? }
7784
if options.redux?
7885
invoke "react_on_rails:react_with_redux", [], { typescript: options.typescript? }
7986
else
@@ -424,6 +431,7 @@ def add_js_dependencies
424431
add_react_on_rails_package
425432
add_react_dependencies
426433
add_css_dependencies
434+
add_rspack_dependencies if options.rspack?
427435
add_dev_dependencies
428436
end
429437

@@ -489,12 +497,36 @@ def add_css_dependencies
489497
handle_npm_failure("CSS dependencies", css_deps) unless success
490498
end
491499

500+
def add_rspack_dependencies
501+
puts "Installing Rspack core dependencies..."
502+
rspack_deps = %w[
503+
@rspack/core
504+
rspack-manifest-plugin
505+
]
506+
if add_npm_dependencies(rspack_deps)
507+
@added_dependencies_to_package_json = true
508+
return
509+
end
510+
511+
success = system("npm", "install", *rspack_deps)
512+
@ran_direct_installs = true if success
513+
handle_npm_failure("Rspack dependencies", rspack_deps) unless success
514+
end
515+
492516
def add_dev_dependencies
493517
puts "Installing development dependencies..."
494-
dev_deps = %w[
495-
@pmmmwh/react-refresh-webpack-plugin
496-
react-refresh
497-
]
518+
dev_deps = if options.rspack?
519+
%w[
520+
@rspack/cli
521+
@rspack/plugin-react-refresh
522+
react-refresh
523+
]
524+
else
525+
%w[
526+
@pmmmwh/react-refresh-webpack-plugin
527+
react-refresh
528+
]
529+
end
498530
if add_npm_dependencies(dev_deps, dev: true)
499531
@added_dependencies_to_package_json = true
500532
return

0 commit comments

Comments
 (0)