Re.Pack 3.x to 4.x

This guide shows how to upgrade from @callstack/repack@3.x to @callstack/repack@4.x.

If you are upgrading from any version prior to 3.x, we recommend that you follow other migration guides first.

Thanks for using Re.Pack!

If you are still facing some build or runtime errors after the migration, please open an issue.

Node version

Re.Pack 4.x requires Node.js >= 18.0.0 or newer. Make sure to update your Node.js version before upgrading to 4.x.

Dependencies

First, you need to update dependencies in your project's package.json:

npm
yarn
pnpm
bun
npm install -D @callstack/repack@^4.0.0
TIP

It's also recommended to update Webpack to the latest version.

Webpack config

Every project is different and we have no way of knowing what your Webpack config looks like, so we cannot pin point every possible change you might need to do. Instead we provide a diffs of our Webpack config templates, which you can use as a base to figure out what you need to change.

Below are the diffs of both .mjs and .cjs versions of webpack.config:

MJS Diff
diff --git a/templates/webpack.config.mjs b/templates/webpack.config.mjs
index 3589166..4f19a6f 100644
--- a/templates/webpack.config.mjs
+++ b/templates/webpack.config.mjs
@@ -1,13 +1,17 @@
-import path from 'path';
+import { createRequire } from 'node:module';
+import path from 'node:path';
 import TerserPlugin from 'terser-webpack-plugin';
 import * as Repack from '@callstack/repack';
 
+const dirname = Repack.getDirname(import.meta.url);
+const { resolve } = createRequire(import.meta.url);
+
 /**
  * More documentation, installation, usage, motivation and differences with Metro is available at:
  * https://github.com/callstack/repack/blob/main/README.md
  *
  * The API documentation for the functions and plugins used in this file is available at:
- * https://re-pack.netlify.app/
+ * https://re-pack.dev
  */
 
 /**
@@ -20,7 +24,7 @@ import * as Repack from '@callstack/repack';
 export default (env) => {
   const {
     mode = 'development',
-    context = Repack.getDirname(import.meta.url),
+    context = dirname,
     entry = './index.js',
     platform = process.env.PLATFORM,
     minimize = mode === 'production',
@@ -28,16 +32,14 @@ export default (env) => {
     bundleFilename = undefined,
     sourceMapFilename = undefined,
     assetsPath = undefined,
-    reactNativePath = new URL('./node_modules/react-native', import.meta.url)
-      .pathname,
+    reactNativePath = resolve('react-native'),
   } = env;
-  const dirname = Repack.getDirname(import.meta.url);
 
   if (!platform) {
     throw new Error('Missing platform');
   }
 
-    /**
+  /**
    * Using Module Federation might require disabling hmr.
    * Uncomment below to set `devServer.hmr` to `false`.
    *
@@ -49,7 +51,7 @@ export default (env) => {
   // if (devServer) {
   //   devServer.hmr = false;
   // }
-  
+
   /**
    * Depending on your Babel configuration you might want to keep it.
    * If you don't use `env` in your Babel config, you can remove it.
@@ -106,6 +108,7 @@ export default (env) => {
      */
     output: {
       clean: true,
+      hashFunction: 'xxhash64',
       path: path.join(dirname, 'build/generated', platform),
       filename: 'index.bundle',
       chunkFilename: '[name].chunk.bundle',
@@ -147,17 +150,17 @@ export default (env) => {
        */
       rules: [
         {
-          test: /\.[jt]sx?$/,
+          test: /\.[cm]?[jt]sx?$/,
           include: [
-            /node_modules(.*[/\\])+react/,
+            /node_modules(.*[/\\])+react-native/,
             /node_modules(.*[/\\])+@react-native/,
             /node_modules(.*[/\\])+@react-navigation/,
             /node_modules(.*[/\\])+@react-native-community/,
-            /node_modules(.*[/\\])+@expo/,
+            /node_modules(.*[/\\])+expo/,
             /node_modules(.*[/\\])+pretty-format/,
             /node_modules(.*[/\\])+metro/,
             /node_modules(.*[/\\])+abort-controller/,
-            /node_modules(.*[/\\])+@callstack\/repack/,
+            /node_modules(.*[/\\])+@callstack[/\\]repack/,
           ],
           use: 'babel-loader',
         },
@@ -185,7 +188,7 @@ export default (env) => {
          * This loader handles all static assets (images, video, audio and others), so that you can
          * use (reference) them inside your application.
          *
-         * If you wan to handle specific asset type manually, filter out the extension
+         * If you want to handle specific asset type manually, filter out the extension
          * from `ASSET_EXTENSIONS`, for example:
          * ```
          * Repack.ASSET_EXTENSIONS.filter((ext) => ext !== 'svg')
CJS Diff
diff --git a/templates/webpack.config.cjs b/templates/webpack.config.cjs
index af917c4..cf0644a 100644
--- a/templates/webpack.config.cjs
+++ b/templates/webpack.config.cjs
@@ -7,7 +7,7 @@ const Repack = require('@callstack/repack');
  * https://github.com/callstack/repack/blob/main/README.md
  *
  * The API documentation for the functions and plugins used in this file is available at:
- * https://re-pack.netlify.app/
+ * https://re-pack.dev
  */
 
 /**
@@ -22,7 +22,7 @@ module.exports = (env) => {
     mode = 'development',
     context = __dirname,
     entry = './index.js',
-    platform,
+    platform = process.env.PLATFORM,
     minimize = mode === 'production',
     devServer = undefined,
     bundleFilename = undefined,
@@ -104,7 +104,8 @@ module.exports = (env) => {
      */
     output: {
       clean: true,
-      path: path.join(dirname, 'build/generated', platform),
+      hashFunction: 'xxhash64',
+      path: path.join(__dirname, 'build/generated', platform),
       filename: 'index.bundle',
       chunkFilename: '[name].chunk.bundle',
       publicPath: Repack.getPublicPath({ platform, devServer }),
@@ -145,17 +146,17 @@ module.exports = (env) => {
        */
       rules: [
         {
-          test: /\.[jt]sx?$/,
+          test: /\.[cm]?[jt]sx?$/,
           include: [
-            /node_modules(.*[/\\])+react/,
+            /node_modules(.*[/\\])+react-native/,
             /node_modules(.*[/\\])+@react-native/,
             /node_modules(.*[/\\])+@react-navigation/,
             /node_modules(.*[/\\])+@react-native-community/,
-            /node_modules(.*[/\\])+@expo/,
+            /node_modules(.*[/\\])+expo/,
             /node_modules(.*[/\\])+pretty-format/,
             /node_modules(.*[/\\])+metro/,
             /node_modules(.*[/\\])+abort-controller/,
-            /node_modules(.*[/\\])+@callstack\/repack/,
+            /node_modules(.*[/\\])+@callstack[/\\]repack/,
           ],
           use: 'babel-loader',
         },
@@ -183,7 +184,7 @@ module.exports = (env) => {
          * This loader handles all static assets (images, video, audio and others), so that you can
          * use (reference) them inside your application.
          *
-         * If you wan to handle specific asset type manually, filter out the extension
+         * If you want to handle specific asset type manually, filter out the extension
          * from `ASSET_EXTENSIONS`, for example:
          * ```
          * Repack.ASSET_EXTENSIONS.filter((ext) => ext !== 'svg')

After applying these changes to your Webpack config, try bundling your application or running it. If you have build errors try adjusting Webpack config based on what the errors are saying.

After addressing build errors, you should be able to run the application.

CLI commands changes

Re.Pack commands now override the default react-native start and react-native bundle Community CLI commands starting from React Native >= 0.74.0.

This change primarily impacts setups where both Metro and Re.Pack are used simultaneously.

To maintain your current workflow without adopting these overrides, especially to avoid conflicts in projects using both Metro and Re.Pack, you can opt out by filtering out the new command names and reverting to the legacy webpack prefixed commands:

react-native.config.js
const repackCommands = require('@callstack/repack/commands');

module.exports = {
  commands: repackCommands.filter((command) =>
    command.name.startsWith('webpack')
  ),
};
INFO

As a side effect of this change, running react-native run-ios or react-native run-android will now launch the Re.Pack dev server by default instead of the Metro dev server.

Module resolution changes

We've investigated differences between Metro and Webpack module resolution and made getResolveOptions compatible with metro-resolver and @react-native/metro-config

You might want to revisit your configuration if you were relying on one of these specific behaviours:

  1. Order of extensions was changed to match the order from @react-native/metro-config. The only exception is .json extension which is always resolved last.
  2. Resolution via Package Exports (using exports field in package.json) is now optional and disabled by default. It can now be enabled via getResolveOptions options parameter. This change was introduced to match metro defaults.
  3. Default conditionNames are now: ['require', 'import', 'react-native'] and match @react-native/metro-config defaults.

ReactRefreshPlugin removal

In Re.Pack 4.x we've removed ReactRefreshPlugin entirely. If you were using ReactRefreshPlugin, you should replace it with DevelopmentPlugin.

CodeSigningPlugin changes

CodeSigningPlugin no longer accepts outputPath property as configuration option. Instead, it performs the code-signing in-place and integrates nicely with OutputPlugin. You should remove outputPath from your CodeSigningPlugin configuration.