Re.Pack 1.x to 2.x

This guide shows how to upgrade from @callstack/nativepack@1.x to @callstack/repack@2.x.

INFO

@callstack/repack is rebranded @callstack/nativepack - they are both the same project.

Dependencies

First, you need to update dependencies and replace @callstack/nativepack with @callstack/repack in your project's package.json:

- "@callstack/nativepack": "^1.4.2"
+ "@callstack/repack": "^2.0.0"
TIP

It's also recommended to update Webpack to the latest version. In case of any trouble, take a look at Compatibility with Webpack table.

After updating dependencies, please run:

npm install
# or
yarn install

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 diff of our Webpack config template, which you can use as a base to figure out what you need to change.

Below is the diff of template/webpack.config.js between Re.Pack 1.x and 2.x:

diff --git a/templates/webpack.config.js b/templates/webpack.config.js
index 8373e9e..6ff525d 100644
--- a/templates/webpack.config.js
+++ b/templates/webpack.config.js
@@ -1,82 +1,44 @@
+const path = require('path');
 const webpack = require('webpack');
-const {
-  parseCliOptions,
-  getInitializationEntries,
-  getResolveOptions,
-  ReactNativeAssetsPlugin,
-  LoggerPlugin,
-  DevServerPlugin,
-  DEFAULT_PORT,
-  ReactNativeTargetPlugin,
-  getPublicPath,
-  getChunkFilename,
-} = require('@callstack/nativepack');
 const TerserPlugin = require('terser-webpack-plugin');
+const ReactNative = require('@callstack/repack');
 
 /**
  * More documentation, installation, usage, motivation and differences with Metro is available at:
- * https://github.com/callstack/nativepack/blob/main/README.md
+ * 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://callstack-nativepack.netlify.app/
+ * https://re-pack.netlify.app/
  */
 
 /**
  * This is the Webpack configuration file for your React Native project.
  * It can be used in 2 ways:
  * - by running React Native CLI eg: `npx react-native start` or `npx react-native bundle`
- * - by running Webpack CLI eg: `npx webpack-cli -c webpack.config.js`
+ * - by running Webpack CLI eg: `PLATFORM=(ios|android) npx webpack-cli -c webpack.config.js`
  *
  * Depending on which option you chose the output might be different, since when running with
- * React Native CLI most of the values from `parseCliOptions` will be filled in by React Native CLI.
+ * React Native CLI most of the values from `getMode`, `getPlatform`, etc. will be filled in by React Native CLI.
  * However, when running with Webpack CLI, you might want to tweak `fallback` values to your liking.
  *
  * Please refer to the API documentation for list of options, plugins and their descriptions.
  */
 
 /**
- * Get options from React Native CLI when Webpack is run from `react-native start` or `react-native bundle`
+ * Get options from React Native CLI when Webpack is run from `react-native start` or `react-native bundle`.
  *
- * If you run Webpack using Webpack CLI the default and fallback values will be used - use `fallback`
- * to specify your values if the default's doesn't suit your project.
+ * If you run Webpack using Webpack CLI, the values from `fallback` will be used - use it
+ * to specify your values, if the defaults don't suit your project.
  */
-const {
-  dev,
-  mode,
-  context,
-  entry,
-  platform,
-  reactNativePath,
-  outputPath,
-  outputFilename,
-  devServer,
-  sourcemapFilename,
-  minimize,
-} = parseCliOptions({
-  fallback: {
-    /**
-     * Fallback to production when running with Webpack CLI.
-     */
-    mode: 'production',
-    /**
-     * Make sure you always specify platform when running with Webpack CLI.
-     * Alternatively you could use `process.env.PLATFORM` and run:
-     * `PLATFORM=ios npx webpack-cli -c webpack.config.js`
-     */
-    platform: 'ios',
-    devServer: { port: DEFAULT_PORT },
-  },
-});
-
-/**
- * Enable development server in development mode.
- */
-const devServerEnabled = dev;
 
-/**
- * Enable Hot Module Replacement with React Refresh in when development server is running.
- */
-const hmr = devServerEnabled;
+const mode = ReactNative.getMode({ fallback: 'development' });
+const dev = mode === 'development';
+const context = ReactNative.getContext();
+const entry = ReactNative.getEntry();
+const platform = ReactNative.getPlatform({ fallback: process.env.PLATFORM });
+const minimize = ReactNative.isMinimizeEnabled({ fallback: !dev });
+const devServer = ReactNative.getDevServerOptions();
+const reactNativePath = ReactNative.getReactNativePath();
 
 /**
  * Depending on your Babel configuration you might want to keep it.
@@ -104,7 +66,12 @@ module.exports = {
    * If you don't want to use Hot Module Replacement, set `hmr` option to `false`. By default,
    * HMR will be enabled in development mode.
    */
-  entry: [...getInitializationEntries(reactNativePath, { hmr }), entry],
+  entry: [
+    ...ReactNative.getInitializationEntries(reactNativePath, {
+      hmr: devServer.hmr,
+    }),
+    entry,
+  ],
   resolve: {
     /**
      * `getResolveOptions` returns additional resolution configuration for React Native.
@@ -112,7 +79,7 @@ module.exports = {
      * convention and some 3rd-party libraries that specify `react-native` field
      * in their `package.json` might not work correctly.
      */
-    ...getResolveOptions(platform),
+    ...ReactNative.getResolveOptions(platform),
 
     /**
      * Uncomment this to ensure all `react-native*` imports will resolve to the same React Native
@@ -125,20 +92,17 @@ module.exports = {
   },
   /**
    * Configures output.
-   * Unless you don't want to use output values passed from React Native CLI, it's recommended to
-   * leave it as it is.
+   * It's recommended to leave it as it is unless you know what you're doing.
+   * By default Webpack will emit files into the directory specified under `path`. In order for the
+   * React Native app use them when bundling the `.ipa`/`.apk`, they need to be copied over with
+   * `ReactNative.OutputPlugin`, which is configured by default.
    */
   output: {
-    path: outputPath,
-    filename: outputFilename,
-    chunkFilename: getChunkFilename({
-      platform,
-      outputFilename,
-    }),
-    publicPath: getPublicPath({
-      devServerEnabled,
-      ...devServer,
-    }),
+    clean: true,
+    path: path.join(__dirname, 'build', platform),
+    filename: 'index.bundle',
+    chunkFilename: '[name].chunk.bundle',
+    publicPath: ReactNative.getPublicPath(devServer),
   },
   /**
    * Configures optimization of the built bundle.
@@ -156,6 +120,11 @@ module.exports = {
          * differently.
          */
         extractComments: false,
+        terserOptions: {
+          format: {
+            comments: false,
+          },
+        },
       }),
     ],
   },
@@ -180,7 +149,7 @@ module.exports = {
           /node_modules(.*[/\\])+pretty-format/,
           /node_modules(.*[/\\])+metro/,
           /node_modules(.*[/\\])+abort-controller/,
-          /node_modules(.*[/\\])+@callstack[/\\]nativepack/,
+          /node_modules(.*[/\\])+@callstack[/\\]repack/,
         ],
         use: 'babel-loader',
       },
@@ -197,7 +166,35 @@ module.exports = {
           loader: 'babel-loader',
           options: {
             /** Add React Refresh transform only when HMR is enabled. */
-            plugins: hmr ? ['module:react-refresh/babel'] : undefined,
+            plugins: devServer.hmr ? ['module:react-refresh/babel'] : undefined,
+          },
+        },
+      },
+      /**
+       * 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
+       * from `ASSET_EXTENSIONS`, for example:
+       * ```
+       * ReactNative.ASSET_EXTENSIONS.filter((ext) => ext !== 'svg')
+       * ```
+       */
+      {
+        test: ReactNative.getAssetExtensionsRegExp(
+          ReactNative.ASSET_EXTENSIONS
+        ),
+        use: {
+          loader: '@callstack/repack/assets-loader',
+          options: {
+            platform,
+            devServerEnabled: devServer.enabled,
+            /**
+             * Defines which assets are scalable - which assets can have
+             * scale suffixes: `@1x`, `@2x` and so on.
+             * By default all images are scalable.
+             */
+            scalableAssetExtensions: ReactNative.SCALABLE_ASSETS,
           },
         },
       },
@@ -209,20 +206,15 @@ module.exports = {
      * to distinguish between production and development
      */
     new webpack.DefinePlugin({
-      'process.env': {
-        NODE_ENV: JSON.stringify(mode),
-      },
       __DEV__: JSON.stringify(dev),
     }),
 
     /**
-     * This plugin makes sure you can use assets like images, videos, audio.
+     * This plugin makes sure the resolution for assets like images works with scales,
+     * for example: `image@1x.png`, `image@2x.png`.
      */
-    new ReactNativeAssetsPlugin({
+    new ReactNative.AssetsResolverPlugin({
       platform,
-      context,
-      outputPath,
-      devServerEnabled,
     }),
 
     /**
@@ -230,16 +222,27 @@ module.exports = {
      * from Web or Node.js. This plugin ensures everything is setup correctly so that features
      * like Hot Module Replacement will work correctly.
      */
-    new ReactNativeTargetPlugin(),
+    new ReactNative.TargetPlugin(),
+
+    /**
+     * By default Webpack will emit files into `output.path` directory (eg: `<root>/build/ios`),
+     * but in order to for the React Native application to include those files (or a subset of those)
+     * they need to be copied over to correct output directories supplied from React Native CLI
+     * when bundling the code (with `webpack-bundle` command).
+     * All remote chunks will be placed under `remoteChunksOutput` directory (eg: `<root>/build/<platform>/remote` by default).
+     * In development mode (when development server is running), this plugin is a no-op.
+     */
+    new ReactNative.OutputPlugin({
+      platform,
+      devServerEnabled: devServer.enabled,
+      remoteChunksOutput: path.join(__dirname, 'build', platform, 'remote'),
+    }),
 
     /**
      * Runs development server when running with React Native CLI start command or if `devServer`
      * was provided as s `fallback`.
      */
-    new DevServerPlugin({
-      enabled: devServerEnabled,
-      hmr,
-      context,
+    new ReactNative.DevServerPlugin({
       platform,
       ...devServer,
     }),
@@ -254,7 +257,7 @@ module.exports = {
     new webpack.SourceMapDevToolPlugin({
       test: /\.(js)?bundle$/,
       exclude: /\.chunk\.(js)?bundle$/,
-      filename: sourcemapFilename,
+      filename: '[file].map',
       append: `//# sourceMappingURL=[url]?platform=${platform}`,
       /**
        * Uncomment for faster builds but less accurate Source Maps
@@ -284,16 +287,16 @@ module.exports = {
      * It's recommended to always have this plugin, otherwise it might be difficult
      * to figure out what's going on when bundling or running development server.
      */
-    new LoggerPlugin({
+    new ReactNative.LoggerPlugin({
       platform,
-      devServerEnabled,
+      devServerEnabled: devServer.enabled,
       output: {
         console: true,
         /**
          * Uncomment for having logs stored in a file to this specific compilation.
          * Compilation for each platform gets it's own log file.
          */
-        // file: path.join(__dirname, '${build}.${platform}.log`),
+        // file: path.join(__dirname, `${mode}.${platform}.log`),
       },
     }),
   ],
TIP

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.

CAUTION

If you are facing runtime errors or crashes, please open an issue!