Class: Shakapacker::SwcMigrator

Inherits:
Object
  • Object
show all
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
DEFAULT_SWC_CONFIG =
<<~JS.freeze
  // config/swc.config.js
  // This file is merged with Shakapacker's default SWC configuration
  // See: https://swc.rs/docs/configuration/compilation

  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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root_path, logger: nil) ⇒ SwcMigrator

Returns a new instance of SwcMigrator.



73
74
75
76
# File 'lib/shakapacker/swc_migrator.rb', line 73

def initialize(root_path, logger: nil)
  @root_path = Pathname.new(root_path)
  @logger = logger || Logger.new($stdout)
end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



9
10
11
# File 'lib/shakapacker/swc_migrator.rb', line 9

def logger
  @logger
end

#root_pathObject (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



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/shakapacker/swc_migrator.rb', line 118

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_packagesObject



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/shakapacker/swc_migrator.rb', line 151

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.message}"
    []
  end
end

#migrate_to_swc(run_installer: true) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/shakapacker/swc_migrator.rb', line 78

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:"
  logger.info "   - config/swc.config.js is merged with Shakapacker's default SWC configuration"
  logger.info "   - You can customize config/swc.config.js to add additional options"
  logger.info "   - Avoid using .swcrc as it overrides Shakapacker defaults completely"

  # 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 "   rails shakapacker:clean_babel_packages"
  end

  # Suggest running doctor to verify configuration
  logger.info "\n๐Ÿฉบ Run 'rails 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