callstack / repack

A Webpack-based toolkit to build your React Native application with full support of Webpack ecosystem.
https://re-pack.dev
MIT License
1.45k stars 104 forks source link

App Launch Failure After Upgrading to React Native 0.72.15 #704

Open quan-nh-torus opened 4 weeks ago

quan-nh-torus commented 4 weeks ago

Describe the bug

sendbird_chat

Error when running app:

Exception in native call from JS
com.facebook.react.devsupport.JSException: Can't find variable: exports
                                                                                                        at com.facebook.jni.NativeRunnable.run(Native Method)
                                                                                                        at android.os.Handler.handleCallback(Handler.java:959)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:100)
                                                                                                        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
                                                                                                        at android.os.Looper.loopOnce(Looper.java:232)
                                                                                                        at android.os.Looper.loop(Looper.java:317)
                                                                                                        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:228)
                                                                                                        at java.lang.Thread.run(Thread.java:1012)
                                                                                                    Caused by: com.facebook.jni.CppException: Can't find variable: exports                                                                                                   ./node_modules/@sendbird/chat/message.js@http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.bellepro.app.dev&modulesOnly=false&runModule=true:302722:30
http://localhost:8081/index.bundle?
....

webpack.config.mjs

import { createRequire } from 'node:module';
import path from 'node:path';
import TerserPlugin from 'terser-webpack-plugin';
import * as Repack from '@callstack/repack';
import { deps } from './shared/dependencies.mjs';

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.dev
 */

/**
 * Webpack configuration.
 * You can also export a static object or a function returning a Promise.
 *
 * @param env Environment options passed from either Webpack CLI or React Native CLI
 *            when running with `react-native start/bundle`.
 */
export default (env) => {
  const {
    mode = 'development',
    context = dirname,
    entry = './index.js',
    platform = process.env.PLATFORM,
    minimize = mode === 'production',
    devServer = undefined,
    bundleFilename = undefined,
    sourceMapFilename = undefined,
    assetsPath = undefined,
    reactNativePath = resolve('react-native'),
  } = env;
  if (!platform) {
    throw new Error('Missing platform');
  }

  /**
   * Using Module Federation might require disabling hmr.
   * Uncomment below to set `devServer.hmr` to `false`.
   *
   * Keep in mind that `devServer` object is not available
   * when running `webpack-bundle` command. Be sure
   * to check its value to avoid accessing undefined value,
   * otherwise an error might occur.
   */
  // 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.
   *
   * Keep in mind that if you remove it you should set `BABEL_ENV` or `NODE_ENV`
   * to `development` or `production`. Otherwise your production code might be compiled with
   * in development mode by Babel.
   */
  process.env.BABEL_ENV = mode;

  return {
    mode,
    /**
     * This should be always `false`, since the Source Map configuration is done
     * by `SourceMapDevToolPlugin`.
     */
    devtool: false,
    context,
    /**
     * `getInitializationEntries` will return necessary entries with setup and initialization code.
     * 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: [
      ...Repack.getInitializationEntries(reactNativePath, {
        hmr: devServer && devServer.hmr,
      }),
      entry,
    ],
    resolve: {
      /**
       * `getResolveOptions` returns additional resolution configuration for React Native.
       * If it's removed, you won't be able to use `<file>.<platform>.<ext>` (eg: `file.ios.js`)
       * convention and some 3rd-party libraries that specify `react-native` field
       * in their `package.json` might not work correctly.
       */
      ...Repack.getResolveOptions(platform),
      /**
       * Uncomment this to ensure all `react-native*` imports will resolve to the same React Native
       * dependency. You might need it when using workspaces/monorepos or unconventional project
       * structure. For simple/typical project you won't need it.
       */
      alias: {
        '@src': path.resolve(dirname, 'src'),
        'react-native': reactNativePath,
        '@config': path.resolve(dirname, 'config'),
        pako: new URL('./node_modules/pako', import.meta.url).pathname,
        '@turf': new URL('./node_modules/@turf', import.meta.url).pathname,
        i18next: new URL('./node_modules/i18next', import.meta.url).pathname,
        url: new URL('./node_modules/url', import.meta.url).pathname,
        'amazon-cognito-identity-js': new URL(
          './node_modules/amazon-cognito-identity-js',
          import.meta.url,
        ).pathname,
        '@react-native-async-storage/async-storage': new URL(
          './node_modules/@react-native-async-storage/async-storage',
          import.meta.url,
        ).pathname,
        '@aws-amplify': new URL('./node_modules/@aws-amplify', import.meta.url).pathname,
        'react-native-svg': new URL('./node_modules/react-native-svg', import.meta.url).pathname,
        'libphonenumber-js': new URL('./node_modules/libphonenumber-js', import.meta.url).pathname,
        domhandler: new URL('./node_modules/domhandler', import.meta.url).pathname,
        domutils: new URL('./node_modules/domutils', import.meta.url).pathname,
        htmlparser2: new URL('./node_modules/htmlparser2', import.meta.url).pathname,
        domelementtype: new URL('./node_modules/domelementtype', import.meta.url).pathname,
      },
    },
    /**
     * Configures output.
     * 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
     * `Repack.OutputPlugin`, which is configured by default inside `Repack.RepackPlugin`.
     */
    output: {
      clean: true,
      hashFunction: 'xxhash64',
      path: path.join(dirname, 'build/generated', platform),
      filename: 'index.bundle',
      chunkFilename: '[name].chunk.bundle',
      publicPath: Repack.getPublicPath({ platform, devServer }),
    },
    /**
     * Configures optimization of the built bundle.
     */
    optimization: {
      /** Enables minification based on values passed from React Native CLI or from fallback. */
      minimize,
      /** Configure minimizer to process the bundle. */
      minimizer: [
        new TerserPlugin({
          test: /\.(js)?bundle(\?.*)?$/i,
          /**
           * Prevents emitting text file with comments, licenses etc.
           * If you want to gather in-file licenses, feel free to remove this line or configure it
           * differently.
           */
          extractComments: false,
          terserOptions: {
            format: {
              comments: false,
            },
          },
        }),
      ],
      chunkIds: 'named',
    },
    module: {
      /**
       * This rule will process all React Native related dependencies with Babel.
       * If you have a 3rd-party dependency that you need to transpile, you can add it to the
       * `include` list.
       *
       * You can also enable persistent caching with `cacheDirectory` - please refer to:
       * https://github.com/babel/babel-loader#options
       */
      rules: [
        {
          test: /\.[cm]?[jt]sx?$/,
          include: [
            /node_modules(.*[/\\])+react\//,
            /node_modules(.*[/\\])+react-native/,
            /node_modules(.*[/\\])+@react-native/,
            /node_modules(.*[/\\])+react-freeze/,
            /node_modules(.*[/\\])+@react-native/,
            /node_modules(.*[/\\])+@react-navigation/,
            /node_modules(.*[/\\])+@react-native-community/,
            /node_modules(.*[/\\])+pretty-format/,
            /node_modules(.*[/\\])+abort-controller/,
            /node_modules(.*[/\\])+@callstack\/repack/,
            /node_modules(.*[/\\])+@google-cloud\//,
            /node_modules(.*[/\\])+@react-native-async-storage\//,
            /node_modules(.*[/\\])+@react-native-camera-roll\//,
            /node_modules(.*[/\\])+@react-native-community\//,
            /node_modules(.*[/\\])+@sendbird\/uikit-chat-hooks/,
            /node_modules(.*[/\\])+@sendbird\/uikit-react-native-foundation/,
            /node_modules(.*[/\\])+@sendbird\/uikit-utils/,
            /node_modules(.*[/\\])+@sendbird\/chat/,
            /node_modules(.*[/\\])+@sendbird\/react-native-scrollview-enhancer/,
            /node_modules(.*[/\\])+@sendbird\/uikit-react-native/,
            ....
          ],
          use: 'babel-loader',
        },
        {
          test: /\.[cm]?[jt]sx?$/,
          include: [
            /node_modules(.*[/\\])+@react-native-async-storage\//,
            /node_modules(.*[/\\])+@react-native-camera-roll\//,
            /node_modules(.*[/\\])+@react-native-community\//,
            /node_modules(.*[/\\])+@sendbird\/uikit-chat-hooks/,
            /node_modules(.*[/\\])+@sendbird\/uikit-react-native-foundation/,
            /node_modules(.*[/\\])+@sendbird\/uikit-utils/,
            /node_modules(.*[/\\])+@sendbird\/chat/,
            /node_modules(.*[/\\])+@sendbird\/react-native-scrollview-enhancer/,
            /node_modules(.*[/\\])+@sendbird\/uikit-react-native/,
            ...
          ],
          use: {
            loader: 'babel-loader',
            options: {
              presets: [['@babel/preset-typescript']],
            },
          },
        },
        {
          test: /\.[cm]?[jt]sx?$/,
          include: [
            /node_modules(.*[/\\])+@sendbird\/uikit-chat-hooks/,
            /node_modules(.*[/\\])+@sendbird\/uikit-react-native-foundation/,
            /node_modules(.*[/\\])+@sendbird\/uikit-utils/,
            /node_modules(.*[/\\])+@sendbird\/chat/,
            /node_modules(.*[/\\])+@sendbird\/react-native-scrollview-enhancer/,
            /node_modules(.*[/\\])+@sendbird\/uikit-react-native/,
            ...
          ],
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                ['module:metro-react-native-babel-preset', { disableImportExportTransform: true }],
              ],
            },
          },
        },
        /**
         * Here you can adjust loader that will process your files.
         *
         * You can also enable persistent caching with `cacheDirectory` - please refer to:
         * https://github.com/babel/babel-loader#options
         */
        {
          test: /\.[cm]?[jt]sx?$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              /** Add React Refresh transform only when HMR is enabled. */
              plugins: devServer && 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:
         * ```
         * Repack.ASSET_EXTENSIONS.filter((ext) => ext !== 'svg')
         * ```
         */
        {
          test: Repack.getAssetExtensionsRegExp(Repack.ASSET_EXTENSIONS),
          use: {
            loader: '@callstack/repack/assets-loader',
            options: {
              inline: false, // TODO: host app can set inline asset to false (not parse asset to base64)
              platform,
              devServerEnabled: Boolean(devServer),
              /**
               * Defines which assets are scalable - which assets can have
               * scale suffixes: `@1x`, `@2x` and so on.
               * By default all images are scalable.
               */
              scalableAssetExtensions: Repack.SCALABLE_ASSETS,
            },
          },
        },
      ],
    },
    plugins: [
      /**
       * Configure other required and additional plugins to make the bundle
       * work in React Native and provide good development experience with
       * sensible defaults.
       *
       * `Repack.RepackPlugin` provides some degree of customization, but if you
       * need more control, you can replace `Repack.RepackPlugin` with plugins
       * from `Repack.plugins`.
       */
      new Repack.RepackPlugin({
        context,
        mode,
        platform,
        devServer,
        output: {
          bundleFilename,
          sourceMapFilename,
          assetsPath,
        },
      }),
      new Repack.plugins.ModuleFederationPlugin({
        /**
         * The name of the module is used to identify the module in URLs resolver and imports.
         */
        name: 'bpro_app_modules',

        exposes: {
          './appContainer': './src/app.component',
        },

        /**
         * Shared modules are shared in the share scope.
         * React, React Native and React Navigation should be provided here because there should be only one instance of these modules.
         * Their names are used to match requested modules in this compilation.
         */
        shared: deps,
      }),
    ],
  };
};

System Info

- MacOS
- NodeJS v18.18.2

"react-native": "0.72.15",
"@sendbird/chat": "4.10.0",
...
"@callstack/repack": "^4.0.0",

Re.Pack Version

4.0.0

Reproduction

No response

Steps to reproduce

May you try to init new RN 0.72 and using @sendbird/chat

Could you please help me resolve this issue? If you have any questions or need further information, feel free to contact me. Thank you very much for your assistance.

jbroma commented 4 weeks ago

Hi @quan-nh-torus

an example from sendbird seems overly complicated and involving many other unrelated libraries, could you please create a minimal reproduction repository where the problem is happening? Thanks!

quan-nh-torus commented 3 weeks ago

Hi @jbroma,

Apologies for the delayed response. I’ve created a sample chat project using Sendbird. Could you please review it when you have a moment? Here’s the link: repack-sendbird-example.

Thanks in advance!

jbroma commented 3 weeks ago

Hi @quan-nh-torus,

please apply these changes in webpack.config.mjs to fix the issue you were having:

diff --git a/webpack.config.mjs b/webpack.config.mjs
index 027c45c..27589e9 100644
--- a/webpack.config.mjs
+++ b/webpack.config.mjs
@@ -161,7 +161,6 @@ export default (env) => {
             /node_modules(.*[/\\])+metro/,
             /node_modules(.*[/\\])+abort-controller/,
             /node_modules(.*[/\\])+@callstack[/\\]repack/,
-            /node_modules(.*[/\\])+@sendbird[/\\]chat/,
             /node_modules(.*[/\\])+@sendbird[/\\]uikit-react-native/,
             /node_modules(.*[/\\])+@sendbird[/\\]uikit-chat-hooks/,
             /node_modules(.*[/\\])+@sendbird[/\\]uikit-react-native-foundation/,
@@ -170,6 +169,21 @@ export default (env) => {
           ],
           use: 'babel-loader',
         },
+        {
+          test: /\.js$/,
+          include: [/node_modules(.*[/\\])+@sendbird[/\\]chat/],
+          use: {
+            loader: 'babel-loader',
+            options: {
+              presets: [
+                [
+                  'module:metro-react-native-babel-preset',
+                  {disableImportExportTransform: true},
+                ],
+              ],
+            },
+          },
+        },
         /**
          * Here you can adjust loader that will process your files.
          *