Skip to content

Config diff feature: Compare webpack/rspack configs for migration and troubleshooting #667

@justin808

Description

@justin808

Config Diff Feature: Compare webpack/rspack configs for migration and troubleshooting

Problem Statement

Developers need better tools for migrating and troubleshooting webpack/rspack configurations. Currently, there's no systematic way to:

  1. Understand customizations: Compare a custom webpack config against shakapacker defaults to identify what can leverage defaults vs what must remain custom
  2. Migrate webpack → rspack: Identify incompatibilities, required changes, and optimization opportunities
  3. Debug multi-environment issues: Systematically compare development vs production, client vs server configurations
  4. Plan migrations: Generate actionable checklists for moving from custom webpack to shakapacker + optional rspack

Use Cases

1. Legacy webpack → shakapacker migration

Scenario: Project has heavily customized webpack config
Need: Identify which customizations are already handled by shakapacker defaults
Benefit: Layer customizations on top of shakapacker defaults instead of maintaining entire config

2. webpack → rspack conversion

Scenario: Upgrade to rspack for faster builds
Need: Identify webpack-specific plugins/loaders incompatible with rspack
Benefit: Clear migration path with automated suggestions for equivalents

3. Combined migration

Scenario: Custom webpack → shakapacker + rspack
Need: Layer both transformations systematically
Benefit: Confidence in multi-stage migration plan

4. Multi-environment debugging

Scenario: Production builds work but development doesn't (or vice versa)
Need: Systematic diff of dev vs prod configs
Benefit: Quickly identify environment-specific differences causing issues

Phased Implementation

This feature will be implemented across 5 incremental PRs, each delivering standalone value.


Phase 1: Core Diff Engine (PR #1)

Goal: Basic infrastructure for comparing two config files

New Modules:

  • package/configExporter/configDiffer.ts - Deep object diff algorithm
  • package/configExporter/pathNormalizer.ts - Normalize comparable values
  • package/configExporter/diffTypes.ts - TypeScript interfaces

CLI Additions:

bin/export-bundler-config --compare baseline.yaml current.yaml
bin/export-bundler-config --compare old.yaml new.yaml --diff-format=json|yaml|summary
bin/export-bundler-config --compare old.yaml new.yaml --ignore-path=output.path --ignore-path=devtool

Features:

  • Deep object comparison with path tracking (e.g., module.rules[0].test)
  • Handle arrays with ordered comparison
  • Detect added/modified/removed keys
  • Normalize absolute paths to relative
  • Strip machine-specific values (timestamps, temp dirs)
  • Ignore comments and formatting differences

Output Formats:

  • JSON: Machine-readable structured diff
  • Annotated YAML: Human-readable with inline change markers
  • Summary: Quick overview of changes

Example Output (summary format):

Configuration Comparison
========================
Baseline: webpack-dev-client.yaml
Current:  webpack-dev-client-customized.yaml

Summary: 12 additions, 3 modifications, 0 removals

Added:
  - entry.admin_bundle
  - module.rules[3] (TypeScript loader)
  - module.rules[4] (CSS modules)
  - output.publicPath
  + 8 more...

Modified:
  - devtool: cheap-module-source-map → eval-source-map
  - module.rules[0].test: /\.js$/ → /\.(js|jsx|ts|tsx)$/
  - module.rules[0].use[0].options (added presets)

See detailed diff in: diff-output.yaml

Success Criteria:

  • ✅ Compare any two webpack/rspack YAML configs
  • ✅ Accurate path-level granularity
  • ✅ Handle nested objects, arrays, primitives
  • ✅ Normalize paths and ignore formatting

Phase 2: Baseline Generation (PR #2)

Goal: Generate reference configs on-demand via presets

New Modules:

  • package/configExporter/baselineGenerator.ts - Generate clean baselines
  • package/configExporter/presetConfig.ts - Preset definitions
  • lib/templates/presets/*.yaml - Preset specifications

Preset Definitions:
Each preset is a YAML specification:

# lib/templates/presets/react_on_rails.yaml
name: react_on_rails
description: Shakapacker with react_on_rails gem defaults
shakapacker_version: ">=9.0"
react_on_rails_version: ">=16.0"
required_gems:
  - shakapacker
  - react_on_rails
required_packages:
  - webpack
  - webpack-cli
  - react
  - react-dom
config_overrides:
  javascript_transpiler: babel

CLI Additions:

bin/export-bundler-config --generate-baseline --preset=basic
bin/export-bundler-config --generate-baseline --preset=react_on_rails --bundler=rspack
bin/export-bundler-config --generate-baseline --preset=typescript --cache-dir=./baselines

Baseline Generation Process:

  1. Create temporary directory
  2. Initialize minimal Rails app
  3. Install specified gems/packages from preset
  4. Run bin/export-bundler-config for all 4 environments (dev/prod × client/server)
  5. Save to cache directory (e.g., tmp/baselines/react_on_rails-v9.0/)
  6. Return paths to generated configs

Built-in Presets:

  • basic - Vanilla shakapacker, no frameworks
  • react_on_rails - Shakapacker + react_on_rails defaults
  • typescript - Shakapacker + TypeScript support
  • vue - Shakapacker + Vue.js (future)
  • More can be added by community

Success Criteria:

  • ✅ Generate clean baseline configs for all presets
  • ✅ Baselines cached and reusable
  • ✅ Support both webpack and rspack
  • ✅ Generate all 4 environment combinations

Phase 3: Integrated Workflow (PR #3)

Goal: Seamless "export + diff" in one command

CLI Additions:

# Diff current config against baseline preset
bin/export-bundler-config --diff --preset=react_on_rails

# Use custom baseline directory
bin/export-bundler-config --diff --baseline-dir=./my-baselines

# Add diff to doctor output
bin/export-bundler-config --doctor --show-diff

Workflow:

  1. Auto-detect current project configuration (gems, packages)
  2. Generate or load matching baseline
  3. Export current configs (all 4 environments)
  4. Perform diffs
  5. Generate comprehensive report directory

Output Structure:

config-diff-report-2025-10-08/
├── baseline/
│   ├── webpack-dev-client.yaml
│   ├── webpack-dev-server.yaml
│   ├── webpack-prod-client.yaml
│   └── webpack-prod-server.yaml
├── current/
│   ├── webpack-dev-client.yaml
│   ├── webpack-dev-server.yaml
│   ├── webpack-prod-client.yaml
│   └── webpack-prod-server.yaml
├── diff/
│   ├── dev-client.diff.yaml
│   ├── dev-server.diff.yaml
│   ├── prod-client.diff.yaml
│   └── prod-server.diff.yaml
└── summary.md  # Human-readable report

Summary Report (summary.md):

# Configuration Comparison Report

**Baseline**: shakapacker 9.0 + react_on_rails 16.0
**Current**: my-rails-app
**Generated**: 2025-10-08 19:54:00 UTC

## Summary

| Config | Added | Modified | Removed |
|--------|-------|----------|---------|
| dev-client | 12 | 3 | 0 |
| dev-server | 8 | 2 | 0 |
| prod-client | 15 | 5 | 1 |
| prod-server | 10 | 4 | 0 |

## Key Differences

See detailed diffs in `diff/` directory.

Success Criteria:

  • ✅ Single command exports + diffs all 4 environments
  • ✅ Comprehensive report suitable for code review
  • ✅ Suitable for migration planning and documentation

Phase 4: Semantic Analysis (PR #4)

Goal: Intelligent interpretation of configuration differences

New Modules:

  • package/configExporter/semanticAnalyzer.ts - Categorize changes
  • package/configExporter/impactAssessor.ts - Assess impact levels

Enhanced Diff Structure:

interface DiffChange {
  // Existing fields...
  category: 'loader' | 'plugin' | 'optimization' | 'entry' | 'output' | 'other'
  impact: 'breaking' | 'high' | 'medium' | 'low'
  explanation?: string  // From configDocs.ts
  requiredDeps?: string[]  // npm packages needed
  migration?: string  // Steps for breaking changes
}

CLI Additions:

# Configurable analysis depth
bin/export-bundler-config --diff --analysis=structural      # Level 1: Basic diff
bin/export-bundler-config --diff --analysis=categorized    # Level 2: Group by feature
bin/export-bundler-config --diff --analysis=semantic       # Level 3: With explanations
bin/export-bundler-config --diff --analysis=migration      # Level 4: Breaking change guidance

Semantic Categories:

  • Loaders: Changes to module.rules[].use[]
  • Plugins: Changes to plugins[]
  • Entry Points: Changes to entry.*
  • Output: Changes to output.*
  • Optimization: Changes to optimization.*
  • Dev Server: Changes to devServer.*

Impact Assessment:

  • Breaking: Removes functionality, app won't build/run
  • High: Affects core functionality, requires code changes
  • Medium: Feature additions, optional changes
  • Low: Config tweaks, no functional impact

Enhanced Report Example:

## High-Impact Changes (dev-client)

### ⚠️ BREAKING: Removed Entry Point
- **Path**: `entry.legacy_bundle`
- **Impact**: Application code will not be bundled
- **Action**: Restore entry point or remove references

### 🔧 TypeScript Loader Added
- **Path**: `module.rules[3]`
- **Category**: Loader
- **Impact**: Medium
- **Dependencies**: `ts-loader`, `typescript`
- **Install**: `npm install --save-dev ts-loader typescript`
- **Docs**: https://shakapacker.dev/docs/typescript

### 📦 CSS Modules Configuration
- **Path**: `module.rules[1].use[1].options.modules`
- **Impact**: Medium
- **Changes**:
  - `namedExport`: false → true
  - `exportLocalsConvention`: 'asIs' → 'camelCaseOnly'
- **Migration**: Update imports from `styles['class-name']` to `styles.className`

Success Criteria:

  • ✅ Categorized diffs (loaders, plugins, etc)
  • ✅ Impact levels (breaking, high, medium, low)
  • ✅ Links to configDocs explanations
  • ✅ Required dependencies detected and listed
  • ✅ Breaking changes flagged with migration guidance

Phase 5: webpack→rspack Migration (PR #5)

Goal: Specialized support for webpack → rspack conversion

New Modules:

  • package/configExporter/rspackMigration.ts - Migration logic
  • package/configExporter/compatibilityChecker.ts - Detect incompatibilities

CLI Additions:

# Analyze webpack → rspack migration
bin/export-bundler-config --migrate-to-rspack

# Compare webpack vs rspack configs
bin/export-bundler-config --diff --from=webpack --to=rspack

# Dry run: Show what would change
bin/export-bundler-config --migrate-to-rspack --dry-run

# Apply automated migrations
bin/export-bundler-config --migrate-to-rspack --apply

# Keep both configs for comparison
bin/export-bundler-config --migrate-to-rspack --keep-both

Compatibility Detection:

interface CompatibilityIssue {
  path: string
  issue: 'incompatible' | 'deprecated' | 'suboptimal'
  webpack: any
  rspack: any | null  // null if no direct equivalent
  migration: string
  automated: boolean  // Can be auto-fixed?
}

Known Migration Patterns:

  • MiniCssExtractPlugin → Built-in CSS extraction (experiments.css: true)
  • ForkTsCheckerWebpackPlugin → Built-in type checking
  • babel-loader → Optional builtin:swc-loader (20x faster)
  • DefinePlugin → Same API (no changes needed)
  • Custom plugins → Manual review required

Migration Report Example:

# Webpack → Rspack Migration Analysis

## Compatibility Summary
- ✅ Compatible: 45 settings
- 🔄 Requires Changes: 8 settings
- ⚠️ Manual Review: 3 settings
- 🚀 Optimization Opportunities: 6 settings

## Required Changes

### 1. CSS Extraction (Automated ✓)
**Current**: Using `MiniCssExtractPlugin`
**Rspack**: Built-in CSS extraction
**Action**: Remove plugin, add `experiments: { css: true }`
**Script**: `bin/export-bundler-config --migrate-to-rspack --apply`

### 2. TypeScript Type Checking (Automated ✓)
**Current**: Using `ForkTsCheckerWebpackPlugin`
**Rspack**: Built-in type checking
**Action**: Remove plugin

## Optimization Opportunities

### 🚀 Switch to Built-in SWC (Optional)
**Current**: Using `babel-loader`
**Rspack**: Built-in SWC (20x faster)
**Benefit**: Significantly faster builds
**Trade-off**: Some Babel plugins may lack SWC equivalents

## Manual Review Required

### Custom Plugin: `MyCustomPlugin`
**Status**: No known rspack equivalent
**Action**: Review source, may need rspack rewrite
**Docs**: https://rspack.dev/api/plugins

Success Criteria:

  • ✅ Detect webpack-specific features
  • ✅ Suggest rspack equivalents with steps
  • ✅ Identify optimization opportunities
  • ✅ Auto-apply safe migrations with --apply
  • ✅ Generate comprehensive migration checklist

Technical Design Decisions

Cross-Version Comparison

  • Major version matching: Default behavior matches baselines by major version
  • Allow cross-version: Flag warnings when comparing across major versions
  • Use case: Planning v8 → v9 upgrades

Path Normalization

  • Absolute paths: Converted to relative for comparison
  • Machine-specific values: Stripped (timestamps, temp dirs, user paths)
  • Ignore patterns: Configurable via --ignore-path flag

Baseline Strategy

  • Dynamic generation: Create baselines on-demand via presets
  • Caching: Cache baselines for reuse (configurable directory)
  • Versioning: Baselines tied to shakapacker major version
  • Community presets: Allow custom preset definitions

Multi-Environment Support

All features work across 4 config combinations:

  • Development × Client
  • Development × Server
  • Production × Client
  • Production × Server

Incremental Delivery

  • Each phase delivers standalone value
  • PRs are independently reviewable
  • No breaking changes to existing functionality
  • Backward compatible with current --doctor mode

Open Questions / Future Enhancements

Community Contributions

  • Should presets live in separate repo for community contributions?
  • How to version/maintain community presets?

Integration Points

  • Should --doctor automatically include diff against standard baseline?
  • Integration with CI/CD for config drift detection?
  • VS Code extension for visual diff?

Advanced Features (Post v1.0)

  • Interactive diff mode with accept/reject changes
  • Config migration scripts (auto-apply changes)
  • Diff history tracking (track config evolution over time)
  • AI-powered migration suggestions

Success Metrics

Phase 1 Success

  • Developers can compare any two configs
  • Produces accurate structural diffs
  • Handles all config types (webpack, rspack, dev, prod)

Phase 2 Success

  • Baselines available for common setups
  • Community can define custom presets
  • Baselines cached for performance

Phase 3 Success

  • Single command workflow adoption
  • Reports used in code reviews and documentation

Phase 4 Success

  • Semantic analysis reduces troubleshooting time
  • Breaking changes clearly identified
  • Required dependencies auto-detected

Phase 5 Success

  • Successful webpack → rspack migrations
  • Auto-migration adoption rate
  • Reduction in migration-related issues

Implementation Timeline

Phase Estimated Effort Depends On
Phase 1 1-2 weeks Current PR #647 merged
Phase 2 2-3 weeks Phase 1 merged
Phase 3 1-2 weeks Phase 2 merged
Phase 4 2-3 weeks Phase 3 merged
Phase 5 2-3 weeks Phase 4 merged

Total: ~10-15 weeks for complete implementation


Related Issues / PRs


Labels

enhancement config-exporter multi-phase help wanted good first issue (Phase 1)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions