Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
38 changes: 28 additions & 10 deletions react_on_rails/lib/generators/react_on_rails/base_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def copy_js_bundle_files
end

def copy_webpack_config
puts "Adding Webpack config"
puts "Adding #{options.rspack? ? 'Rspack' : 'Webpack'} config"
base_path = "base/base"
base_files = %w[babel.config.js
config/webpack/clientWebpackConfig.js
Expand All @@ -83,7 +83,9 @@ def copy_webpack_config
config = {
message: "// The source code including full typescript support is available at:"
}
base_files.each { |file| template("#{base_path}/#{file}.tt", file, config) }
base_files.each do |file|
template("#{base_path}/#{file}.tt", destination_config_path(file), config)
end

# Handle webpack.config.js separately with smart replacement
copy_webpack_main_config(base_path, config)
Expand Down Expand Up @@ -155,7 +157,7 @@ def append_to_spec_rails_helper
private

def copy_webpack_main_config(base_path, config)
webpack_config_path = "config/webpack/webpack.config.js"
webpack_config_path = bundler_main_config_path

if File.exist?(webpack_config_path)
existing_content = File.read(webpack_config_path)
Expand All @@ -167,7 +169,7 @@ def copy_webpack_main_config(base_path, config)
# Show what we're doing
puts " #{set_color('replace', :green)} #{webpack_config_path} " \
"(auto-upgrading from standard Shakapacker to React on Rails config)"
template("#{base_path}/#{webpack_config_path}.tt", webpack_config_path, config)
template("#{base_path}/config/webpack/webpack.config.js.tt", webpack_config_path, config)
elsif react_on_rails_config?(existing_content)
puts " #{set_color('identical', :blue)} #{webpack_config_path} " \
"(already React on Rails compatible)"
Expand All @@ -177,29 +179,45 @@ def copy_webpack_main_config(base_path, config)
end
else
# File doesn't exist, create it
template("#{base_path}/#{webpack_config_path}.tt", webpack_config_path, config)
template("#{base_path}/config/webpack/webpack.config.js.tt", webpack_config_path, config)
end
end

def handle_custom_webpack_config(base_path, config, webpack_config_path)
# Custom config - ask user
puts "\n#{set_color('NOTICE:', :yellow)} Your webpack.config.js appears to be customized."
config_file_name = File.basename(webpack_config_path)
bundler_name = options.rspack? ? "rspack" : "webpack"
puts "\n#{set_color('NOTICE:', :yellow)} Your #{config_file_name} appears to be customized."
puts "React on Rails needs to replace it with an environment-specific loader."
puts "Your current config will be backed up to webpack.config.js.backup"
puts "Your current config will be backed up to #{config_file_name}.backup"

if yes?("Replace webpack.config.js with React on Rails version? (Y/n)")
if yes?("Replace #{config_file_name} with React on Rails version? (Y/n)")
# Create backup
backup_path = "#{webpack_config_path}.backup"
if File.exist?(webpack_config_path)
FileUtils.cp(webpack_config_path, backup_path)
puts " #{set_color('create', :green)} #{backup_path} (backup of your custom config)"
end

template("#{base_path}/#{webpack_config_path}.tt", webpack_config_path, config)
template("#{base_path}/config/webpack/webpack.config.js.tt", webpack_config_path, config)
else
puts " #{set_color('skip', :yellow)} #{webpack_config_path}"
puts " #{set_color('WARNING:', :red)} React on Rails may not work correctly " \
"without the environment-specific webpack config"
"without the environment-specific #{bundler_name} config"
end
end

def destination_config_path(path)
return path unless options.rspack?

path.sub("config/webpack/", "config/rspack/")
end

def bundler_main_config_path
if options.rspack?
"config/rspack/rspack.config.js"
else
"config/webpack/webpack.config.js"
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,13 @@ def shakapacker_configured?
# Check for essential shakapacker configuration files and binaries
shakapacker_binaries_exist? &&
File.exist?("config/shakapacker.yml") &&
File.exist?("config/webpack/webpack.config.js")
shakapacker_config_file_exists?
end

def shakapacker_config_file_exists?
File.exist?("config/webpack/webpack.config.js") ||
File.exist?("config/rspack/rspack.config.js") ||
File.exist?("config/rspack/rspack.config.ts")
end

def print_shakapacker_setup_banner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,24 +305,31 @@
end
end

it "generates unified webpack config with bundler detection" do
assert_file "config/webpack/development.js" do |content|
it "generates unified rspack config with bundler detection" do
assert_file "config/rspack/development.js" do |content|
expect(content).to include("const { devServer, inliningCss, config } = require('shakapacker')")
expect(content).to include("if (config.assets_bundler === 'rspack')")
expect(content).to include("@rspack/plugin-react-refresh")
expect(content).to include("@pmmmwh/react-refresh-webpack-plugin")
end
end

it "generates server webpack config with bundler variable" do
assert_file "config/webpack/serverWebpackConfig.js" do |content|
it "generates server rspack config with bundler variable" do
assert_file "config/rspack/serverWebpackConfig.js" do |content|
expect(content).to include("const bundler = config.assets_bundler === 'rspack'")
expect(content).to include("? require('@rspack/core')")
expect(content).to include(": require('webpack')")
expect(content).to include("new bundler.optimize.LimitChunkCountPlugin")
end
end

it "writes the main rspack config to config/rspack/rspack.config.js" do
assert_file "config/rspack/rspack.config.js" do |content|
expect(content).to include("const envSpecificConfig = () =>")
expect(content).to include("const path = resolve(__dirname, `${env.nodeEnv}.js`)")
end
end

it "configures rspack in shakapacker.yml" do
assert_file "config/shakapacker.yml" do |content|
# Should have rspack as the bundler (inherited by all environments via YAML anchor)
Expand Down Expand Up @@ -497,6 +504,32 @@
end
end

describe "#shakapacker_configured?" do
let(:install_generator) { described_class.new }

before do
allow(File).to receive(:exist?).and_call_original
allow(install_generator).to receive(:shakapacker_binaries_exist?).and_return(true)
allow(File).to receive(:exist?).with("config/shakapacker.yml").and_return(true)
end

it "returns true when rspack config exists in config/rspack" do
allow(File).to receive(:exist?).with("config/webpack/webpack.config.js").and_return(false)
allow(File).to receive(:exist?).with("config/rspack/rspack.config.js").and_return(true)
allow(File).to receive(:exist?).with("config/rspack/rspack.config.ts").and_return(false)

expect(install_generator.send(:shakapacker_configured?)).to be true
end

it "returns false when no supported bundler config file exists" do
allow(File).to receive(:exist?).with("config/webpack/webpack.config.js").and_return(false)
allow(File).to receive(:exist?).with("config/rspack/rspack.config.js").and_return(false)
allow(File).to receive(:exist?).with("config/rspack/rspack.config.ts").and_return(false)

expect(install_generator.send(:shakapacker_configured?)).to be false
end
end

# Regression test for https://github.com/shakacode/react_on_rails/issues/2287
# Bundler subprocess commands must run in unbundled environment to prevent
# BUNDLE_GEMFILE inheritance from parent process
Expand Down
Loading