Class: Shakapacker::SwcMigrator
- Inherits:
-
Object
- Object
- Shakapacker::SwcMigrator
- Defined in:
- lib/shakapacker/swc_migrator.rb
Constant Summary collapse
- BABEL_PACKAGES =
Babel packages safe to remove when migrating to SWC Note: @babel/core and @babel/eslint-parser are excluded as they may be needed for ESLint
[ "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-object-rest-spread", "@babel/plugin-syntax-dynamic-import", "@babel/plugin-transform-destructuring", "@babel/plugin-transform-regenerator", "@babel/plugin-transform-runtime", "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript", "@babel/runtime", "babel-loader", "babel-plugin-macros", "babel-plugin-transform-react-remove-prop-types" ].freeze
- ESLINT_BABEL_PACKAGES =
Babel packages that may be needed for ESLint - only remove if user explicitly confirms
[ "@babel/core", "@babel/eslint-parser" ].freeze
- SWC_PACKAGES =
{ "@swc/core" => "^1.7.39", "swc-loader" => "^0.2.6" }.freeze
- ESLINT_CONFIG_FILES =
%w[ .eslintrc .eslintrc.js .eslintrc.cjs .eslintrc.yaml .eslintrc.yml .eslintrc.json ].freeze
- SWC_CONFIG_WEBPACK_HEADER =
<<~JS.freeze // config/swc.config.js // This file is merged with Shakapacker's default SWC configuration // See: https://swc.rs/docs/configuration/compilation JS
- SWC_CONFIG_RSPACK_HEADER =
Rspack never reads config/swc.config.js: package/rules/rspack.ts sets its builtin:swc-loader options inline, so the file is generated with a header that says so.
The example keeps
test: 'match'and overrides the JS and TS rules separately on purpose. Matching onuse.loaderalone (a single override with notest) looks tidier but is wrong: webpack-merge cannot pair such an override with a specific rule, so it appends builtin:swc-loader onto EVERY rule'susechain, including the CSS and Sass rules. Verified at webpack-merge 5.10.0 and 6.0.1. <<~JS.freeze // config/swc.config.js // NOTE: This file is NOT read when assets_bundler is 'rspack'. // Shakapacker's Rspack config sets its 'builtin:swc-loader' options inline, so nothing // here reaches your build. To customize SWC for Rspack, apply webpack-merge's // mergeWithRules to the OUTPUT of generateRspackConfig(). Passing an override into // generateRspackConfig() cannot replace the built-in rule: it merges with plain merge, // which concatenates module.rules and leaves the built-in rule in place alongside yours. // // Use options: 'merge', not 'replace'. 'merge' deep-merges into the built-in options so // parser.jsx (JS), parser.syntax/tsx (TS), and transform.react.runtime survive. // 'replace' swaps the whole options object and silently drops them, breaking JSX and // TypeScript compilation. // // Keep test: 'match' in the merge spec. Matching on use.loader alone appends // builtin:swc-loader to every rule's use chain, CSS and Sass included. // // const { mergeWithRules } = require('shakapacker'); // const { generateRspackConfig } = require('shakapacker/rspack'); // // const swcOptions = { jsc: { keepClassNames: true } }; // your SWC options // // module.exports = mergeWithRules({ // module: { rules: { test: 'match', use: { loader: 'match', options: 'merge' } } }, // })(generateRspackConfig(), { // module: { // rules: [ // // Shakapacker registers separate builtin:swc-loader rules for JS and TS. // // Override both: a JS-only override never reaches .ts/.tsx files. // { // test: /\\.(js|jsx|mjs)$/, // use: [{ loader: 'builtin:swc-loader', options: swcOptions }], // }, // { // test: /\\.(ts|tsx)$/, // use: [{ loader: 'builtin:swc-loader', options: swcOptions }], // }, // ], // }, // }); // // See: https://github.com/shakacode/shakapacker/blob/main/docs/rspack.md JS
- SWC_CONFIG_BODY =
<<~JS.freeze const { env } = require('shakapacker'); module.exports = { options: { jsc: { // CRITICAL for Stimulus compatibility: Prevents SWC from mangling class names // which breaks Stimulus's class-based controller discovery mechanism keepClassNames: true, transform: { react: { runtime: 'automatic', refresh: env.isDevelopment && env.runningWebpackDevServer, }, }, }, }, }; JS
- DEFAULT_SWC_CONFIG =
Kept for backward compatibility: same value as before, and still the webpack variant.
"#{SWC_CONFIG_WEBPACK_HEADER}\n#{SWC_CONFIG_BODY}".freeze
- RSPACK_SWC_CONFIG =
"#{SWC_CONFIG_RSPACK_HEADER}\n#{SWC_CONFIG_BODY}".freeze
Instance Attribute Summary collapse
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
-
#root_path ⇒ Object
readonly
Returns the value of attribute root_path.
Instance Method Summary collapse
- #clean_babel_packages(run_installer: true) ⇒ Object
- #find_babel_packages ⇒ Object
-
#initialize(root_path, logger: nil) ⇒ SwcMigrator
constructor
A new instance of SwcMigrator.
- #migrate_to_swc(run_installer: true) ⇒ Object
Constructor Details
#initialize(root_path, logger: nil) ⇒ SwcMigrator
Returns a new instance of SwcMigrator.
132 133 134 135 |
# File 'lib/shakapacker/swc_migrator.rb', line 132 def initialize(root_path, logger: nil) @root_path = Pathname.new(root_path) @logger = logger || Logger.new($stdout) end |
Instance Attribute Details
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
9 10 11 |
# File 'lib/shakapacker/swc_migrator.rb', line 9 def logger @logger end |
#root_path ⇒ Object (readonly)
Returns the value of attribute root_path.
9 10 11 |
# File 'lib/shakapacker/swc_migrator.rb', line 9 def root_path @root_path end |
Instance Method Details
#clean_babel_packages(run_installer: true) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/shakapacker/swc_migrator.rb', line 175 def clean_babel_packages(run_installer: true) logger.info "๐งน Removing Babel packages..." package_json_path = root_path.join("package.json") unless package_json_path.exist? logger.error "โ No package.json found" return { removed_packages: [], config_files_deleted: [], preserved_packages: [] } end # Check if ESLint uses Babel parser preserved_for_eslint = [] if eslint_uses_babel? logger.info "\nโ ๏ธ ESLint configuration detected that uses Babel parser" logger.info " Preserving @babel/core and @babel/eslint-parser for ESLint compatibility" logger.info " To switch ESLint parser:" logger.info " 1. For TypeScript: use @typescript-eslint/parser" logger.info " 2. For JavaScript: use espree (ESLint's default parser)" preserved_for_eslint = ESLINT_BABEL_PACKAGES end removed_packages = remove_babel_from_package_json(package_json_path, preserve: preserved_for_eslint) deleted_files = delete_babel_config_files if removed_packages.any? logger.info "โ Babel packages removed successfully!" run_package_manager_install if run_installer else logger.info "โน๏ธ No Babel packages found to remove" end { removed_packages: removed_packages, config_files_deleted: deleted_files, preserved_packages: preserved_for_eslint } end |
#find_babel_packages ⇒ Object
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/shakapacker/swc_migrator.rb', line 208 def find_babel_packages package_json_path = root_path.join("package.json") return [] unless package_json_path.exist? begin package_json = JSON.parse(File.read(package_json_path)) dependencies = package_json["dependencies"] || {} dev_dependencies = package_json["devDependencies"] || {} all_deps = dependencies.merge(dev_dependencies) # Find all babel packages (including ESLint-related ones for display) all_babel_packages = BABEL_PACKAGES + ESLINT_BABEL_PACKAGES found_packages = all_babel_packages.select { |pkg| all_deps.key?(pkg) } found_packages rescue JSON::ParserError => e logger.error "Failed to parse package.json: #{e.}" [] end end |
#migrate_to_swc(run_installer: true) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/shakapacker/swc_migrator.rb', line 137 def migrate_to_swc(run_installer: true) logger.info "๐ Starting migration from Babel to SWC..." results = { config_updated: update_shakapacker_config, packages_installed: install_swc_packages, swc_config_created: create_swc_config, babel_packages_found: find_babel_packages } logger.info "๐ Migration to SWC complete!" logger.info " Note: SWC is approximately 20x faster than Babel for transpilation." logger.info " Please test your application thoroughly after migration." logger.info "\n๐ Configuration Info:" log_swc_config_guidance # Show cleanup recommendations if babel packages found if results[:babel_packages_found].any? logger.info "\n๐งน Cleanup Recommendations:" logger.info " Found the following Babel packages in your package.json:" results[:babel_packages_found].each do |package| logger.info " - #{package}" end logger.info "\n To remove them, run:" logger.info " bundle exec rake shakapacker:clean_babel_packages" end # Suggest running doctor to verify configuration logger.info "\n๐ฉบ Run 'bundle exec rake shakapacker:doctor' to verify your configuration" # Run package manager install if packages were added if run_installer && results[:packages_installed].any? run_package_manager_install end results end |