Class: Shakapacker::BundlerSwitcher

Inherits:
Object
  • Object
show all
Defined in:
lib/shakapacker/bundler_switcher.rb

Overview

Provides functionality to switch between webpack and rspack bundlers

Constant Summary collapse

SHAKAPACKER_CONFIG =
"config/shakapacker.yml"
CUSTOM_DEPS_CONFIG =
".shakapacker-switch-bundler-dependencies.yml"
ASSETS_BUNDLER_PATTERN =

Regex pattern to detect assets_bundler key in config (only matches uncommented lines)

/^[ \t]*assets_bundler:/
SHARED_DEPS =

Shared dependencies used by both webpack and rspack These should not be removed when switching bundlers

{
  dev: %w[],
  prod: %w[webpack-merge]
}.freeze
DEFAULT_RSPACK_DEPS =

Default dependencies for each bundler (package names only, no versions) Note: Excludes independent/optional dependencies like @swc/core, swc-loader (user-configured transpilers)

{
  dev: %w[@rspack/cli @rspack/plugin-react-refresh],
  prod: %w[@rspack/core rspack-manifest-plugin]
}.freeze
DEFAULT_WEBPACK_DEPS =
{
  dev: %w[webpack webpack-cli webpack-dev-server @pmmmwh/react-refresh-webpack-plugin],
  prod: %w[webpack-assets-manifest]
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root_path = nil) ⇒ BundlerSwitcher

Returns a new instance of BundlerSwitcher.



37
38
39
# File 'lib/shakapacker/bundler_switcher.rb', line 37

def initialize(root_path = nil)
  @root_path = root_path || (defined?(Rails) ? Rails.root : Pathname.new(Dir.pwd))
end

Instance Attribute Details

#root_pathObject (readonly)

Returns the value of attribute root_path.



35
36
37
# File 'lib/shakapacker/bundler_switcher.rb', line 35

def root_path
  @root_path
end

Instance Method Details

#current_bundlerObject



41
42
43
44
# File 'lib/shakapacker/bundler_switcher.rb', line 41

def current_bundler
  config = load_yaml_config(config_path)
  config.dig("default", "assets_bundler") || "webpack"
end

#init_configObject



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/shakapacker/bundler_switcher.rb', line 96

def init_config
  if File.exist?(custom_config_path)
    puts "⚠️  #{CUSTOM_DEPS_CONFIG} already exists"
    return
  end

  config = {
    "rspack" => {
      "devDependencies" => DEFAULT_RSPACK_DEPS[:dev],
      "dependencies" => DEFAULT_RSPACK_DEPS[:prod]
    },
    "webpack" => {
      "devDependencies" => DEFAULT_WEBPACK_DEPS[:dev],
      "dependencies" => DEFAULT_WEBPACK_DEPS[:prod]
    }
  }

  File.write(custom_config_path, YAML.dump(config))
  puts "✅ Created #{CUSTOM_DEPS_CONFIG}"
  puts ""
  puts "You can now customize the dependencies for each bundler in this file."
  puts "The script will automatically use these custom dependencies when switching bundlers."
end

#show_usageObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/shakapacker/bundler_switcher.rb', line 120

def show_usage
  current = current_bundler
  puts "Current bundler: #{current}"
  puts ""
  puts "Usage:"
  puts "  rake shakapacker:switch_bundler [webpack|rspack] -- [OPTIONS]"
  puts ""
  puts "Options:"
  puts "  --install-deps    Automatically install/uninstall dependencies"
  puts "  --no-uninstall    Skip uninstalling old bundler packages"
  puts "  --init-config     Create #{CUSTOM_DEPS_CONFIG} with default dependencies"
  puts "  --help, -h        Show this help message"
  puts ""
  puts "Examples:"
  puts "  rake shakapacker:switch_bundler rspack -- --install-deps"
  puts "  rake shakapacker:switch_bundler webpack -- --install-deps --no-uninstall"
  puts "  rake shakapacker:switch_bundler -- --init-config"
end

#switch_to(bundler, install_deps: false, no_uninstall: false) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/shakapacker/bundler_switcher.rb', line 46

def switch_to(bundler, install_deps: false, no_uninstall: false)
  unless %w[webpack rspack].include?(bundler)
    raise ArgumentError, "Invalid bundler: #{bundler}. Must be 'webpack' or 'rspack'"
  end

  current = current_bundler
  config_content = File.read(config_path)
  has_assets_bundler = config_content =~ ASSETS_BUNDLER_PATTERN

  # Early exit if already using the target bundler
  # For webpack: if current is webpack, we're done (key optional due to default)
  # For rspack: requires explicit key to be present
  already_configured = if bundler == "webpack"
    current == bundler
  else
    current == bundler && has_assets_bundler
  end

  if already_configured && !install_deps
    puts "✅ Already using #{bundler}"
    return
  end

  if already_configured && install_deps
    puts "✅ Already using #{bundler} - reinstalling dependencies as requested"
    manage_dependencies(bundler, install_deps, switching: false, no_uninstall: no_uninstall)
    return
  end

  successfully_updated = update_config(bundler, config_content, has_assets_bundler)

  # Verify the update was successful (only if update reported success)
  verify_config_update(bundler) if successfully_updated

  puts "✅ Switched from #{current} to #{bundler}"
  puts ""
  puts "📝 Configuration updated in #{SHAKAPACKER_CONFIG}"

  manage_dependencies(bundler, install_deps, no_uninstall: no_uninstall)

  puts ""
  puts "🎯 Next steps:"
  puts "   1. Restart your dev server: bin/dev"
  puts "   2. Verify build works: bin/shakapacker"
  puts ""
  puts "💡 Tip: Both webpack and rspack can coexist in package.json during migration"
  puts "        Use --install-deps to automatically manage dependencies, or manage manually"
  puts "        Use --no-uninstall to skip removing old bundler packages (faster switching)"
end