Skip to content

[v6] Unify webpack and Rspack start/bundle commands #1421

Description

@dannyhw

Motivation

Re.Pack ships two parallel command trees — packages/repack/src/commands/rspack/ and packages/repack/src/commands/webpack/ — each with its own start, bundle, Compiler and command registry, exposed as separate entrypoints (@callstack/repack/commands/rspack and @callstack/repack/commands/webpack). The config pipeline is already shared in commands/common/, and plugins/loaders are shared with a few isRspackCompiler() branches. The commands are the last unshared piece — so features land on one side only, and behaviour has drifted in user-visible ways.

Part of #1414 (Roadmap to V6), which lists "unify webpack and rspack start/bundle commands".

Duplication

  • rspack/index.ts and webpack/index.ts are byte-identical. Both also register webpack-bundle / webpack-start aliases, so the rspack entrypoint installs commands named webpack-*.
  • bundle.ts and start.ts are copy-paste apart from the bundler literals, experiments.cache vs cache, the Rspack-only setupRspackEnvironment / RSPACK_PROFILE blocks, and the compiler/dev-server wiring (see below).
  • CompilerAsset is defined in both rspack/types.ts and webpack/types.ts.
  • defineRspackConfig / defineWebpackConfig are the same identity function with parallel overload chains and type families.
  • templates/rspack.config.mjs and templates/webpack.config.mjs differ only in doc comments, the define*Config call, and parallel: true on the minimizer (same for the .cjs pair). packages/init downloads one or the other from the repo at init time (createBundlerConfig.ts).

Behavioural divergence

The real cost — the two commands behave differently:

  • Execution model. Rspack: one in-process MultiCompiler, watching all platforms eagerly from compiler.start(). Webpack: a worker_threads worker spawned lazily per platform on the first asset request. Most of the rest follows from this.
  • api.getPlatforms() returns all configured platforms on Rspack, but Object.keys(compiler.workers) on webpack — an empty list until a platform's first bundle request.
  • getSource: the Rspack compiler handles absolute paths (path.isAbsolute check); the webpack one unconditionally joins with rootDir, breaking symbolication for absolute frame paths.
  • Webpack builds the config twice: start.ts calls makeCompilerConfig (for devServer and cache paths), then each worker calls it again in CompilerWorker.ts.
  • isCompilationInProgress is one global boolean on Rspack, a per-platform record on webpack, with slightly different "not found in compilation assets" errors.
  • Progress/build-time reporting flows through ProgressPlugin + the done hook on Rspack, vs worker messages on webpack (which drops reporter progress above 99% in favour of the done message).
  • watchOptions: Rspack applies compilers[0].options.watchOptions to the whole MultiCompiler (per-platform values are ignored); each webpack worker uses its own config's.
  • adb reverse runs from the MultiCompiler watchRun hook on Rspack, and from the watchRun delegate handler in start.ts on webpack.
  • Rspack-only features: --max-workers (documented "(Rspack only)"), experiments.parallelLoader, RSPACK_PROFILE tracing.
  • Config discovery: DEFAULT_RSPACK_CONFIG_LOCATIONS has 6 entries; DEFAULT_WEBPACK_CONFIG_LOCATIONS has 10 (including .webpack/ variants).

Proposed direction

  1. Collapse the two trees into one commands/ implementation parameterised by bundler: 'rspack' | 'webpack', with a small adapter for the compiler factory, ProgressPlugin, cache accessor and profiling. The config layer already threads this parameter everywhere.

  2. Pick one execution model for the dev server and share the Compiler:

    • in-process MultiCompiler for both — simplest, but webpack's CPU-bound compilation for multiple platforms then shares a single thread;
    • worker-per-platform for both — keeps parallelism, but adds IPC and asset copying for Rspack, which is natively parallel already;
    • one Compiler with a pluggable execution strategy — in-process for Rspack, workers for webpack.

    Either way, the dev-server delegate, asset cache, progress and pending-resolver logic should exist once.

  3. Single defineConfig implementation; keep defineRspackConfig / defineWebpackConfig as aliases.

  4. One @callstack/repack/commands entrypoint that detects the bundler, keeping the per-bundler entrypoints as explicit overrides.

  5. Fix the divergences above as part of the merge, rather than porting them across.

Scope

  • Bundler adapter; merge bundle.ts and start.ts into single implementations with one dev-server delegate
  • Unify Compiler: decide the execution model, share asset cache / progress / resolver logic, single CompilerAsset
  • Stop rebuilding the config inside CompilerWorker (pass the resolved config, or drop the worker path)
  • Fix getSource absolute-path handling and api.getPlatforms()
  • Merge the defineConfig implementations and the command registries; decide whether the webpack-* aliases survive v6
  • Bundler-agnostic @callstack/repack/commands entrypoint with detection
  • Decide --max-workers semantics for webpack; unify config file discovery
  • Collapse the templates/ pairs and update packages/init (createBundlerConfig.ts, modifyReactNativeConfig.ts)
  • Keep the describe.each bundler matrix in apps/tester-app/__tests__ green through the merge; add coverage for the divergences above
  • Docs: write getting-started/bundlers.md ([Docs] Getting Started: Rspack & webpack #917) against the unified surface, update the migration guides that reference commands/<bundler>, note the entrypoint change in the v5 → v6 guide

Open questions

  • Which execution model wins? This is the one decision that shapes the rest of the work.
  • Does v6 keep webpack fully co-equal, or best-effort with Rspack as the default? (Roadmap to V6 #1414 targets Rspack 2+.) The answer decides whether the feature gaps get closed or documented.
  • Is a single repack.config.* filename worth the migration churn, given both existing names are established?

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:repackThe issue is about logic/code inside of Re.Pack.type:featureProposal or idea for a new feature or enhancement.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions